samplekit 0.2.0__tar.gz → 1.0.0rc2__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 (165) hide show
  1. samplekit-1.0.0rc2/CHANGELOG.md +653 -0
  2. samplekit-1.0.0rc2/Cargo.lock +3342 -0
  3. samplekit-1.0.0rc2/Cargo.toml +83 -0
  4. samplekit-1.0.0rc2/LICENSE +21 -0
  5. samplekit-1.0.0rc2/PKG-INFO +196 -0
  6. samplekit-1.0.0rc2/README.md +170 -0
  7. samplekit-1.0.0rc2/THIRD-PARTY-LICENSES.md +2496 -0
  8. samplekit-1.0.0rc2/build.rs +87 -0
  9. samplekit-1.0.0rc2/examples/tensile/01-measured/.samplekitrc +1 -0
  10. samplekit-1.0.0rc2/examples/tensile/01-measured/README.md +14 -0
  11. samplekit-1.0.0rc2/examples/tensile/01-measured/samples/aluminium-01.md +12 -0
  12. samplekit-1.0.0rc2/examples/tensile/01-measured/samples/copper-01.md +12 -0
  13. samplekit-1.0.0rc2/examples/tensile/01-measured/samples/steel-01.md +12 -0
  14. samplekit-1.0.0rc2/examples/tensile/02-readings/.samplekitrc +6 -0
  15. samplekit-1.0.0rc2/examples/tensile/02-readings/README.md +13 -0
  16. samplekit-1.0.0rc2/examples/tensile/02-readings/model/specimen.py +21 -0
  17. samplekit-1.0.0rc2/examples/tensile/02-readings/samples/aluminium-01.md +26 -0
  18. samplekit-1.0.0rc2/examples/tensile/02-readings/samples/copper-01.md +26 -0
  19. samplekit-1.0.0rc2/examples/tensile/02-readings/samples/steel-01.md +26 -0
  20. samplekit-1.0.0rc2/examples/tensile/03-curve/.samplekitrc +9 -0
  21. samplekit-1.0.0rc2/examples/tensile/03-curve/README.md +13 -0
  22. samplekit-1.0.0rc2/examples/tensile/03-curve/model/specimen.py +62 -0
  23. samplekit-1.0.0rc2/examples/tensile/03-curve/samples/aluminium-01.md +76 -0
  24. samplekit-1.0.0rc2/examples/tensile/03-curve/samples/copper-01.md +85 -0
  25. samplekit-1.0.0rc2/examples/tensile/03-curve/samples/steel-01.md +79 -0
  26. samplekit-1.0.0rc2/examples/tensile/04-results/.samplekitrc +72 -0
  27. samplekit-1.0.0rc2/examples/tensile/04-results/README.md +18 -0
  28. samplekit-1.0.0rc2/examples/tensile/04-results/machine/aluminium-01/curve.csv +13 -0
  29. samplekit-1.0.0rc2/examples/tensile/04-results/machine/aluminium-01/settings.txt +3 -0
  30. samplekit-1.0.0rc2/examples/tensile/04-results/machine/copper-01/curve.csv +16 -0
  31. samplekit-1.0.0rc2/examples/tensile/04-results/machine/copper-01/settings.txt +3 -0
  32. samplekit-1.0.0rc2/examples/tensile/04-results/machine/steel-01/curve.csv +14 -0
  33. samplekit-1.0.0rc2/examples/tensile/04-results/machine/steel-01/settings.txt +3 -0
  34. samplekit-1.0.0rc2/examples/tensile/04-results/model/specimen.py +169 -0
  35. samplekit-1.0.0rc2/examples/tensile/04-results/photos/steel-01-fracture.jpg +0 -0
  36. samplekit-1.0.0rc2/examples/tensile/04-results/samples/aluminium-01.md +98 -0
  37. samplekit-1.0.0rc2/examples/tensile/04-results/samples/copper-01.md +107 -0
  38. samplekit-1.0.0rc2/examples/tensile/04-results/samples/steel-01.md +100 -0
  39. samplekit-1.0.0rc2/examples/tensile/04-results/scripts/edit.py +16 -0
  40. samplekit-1.0.0rc2/examples/tensile/04-results/scripts/links.py +9 -0
  41. samplekit-1.0.0rc2/examples/tensile/04-results/scripts/model_scripts.py +9 -0
  42. samplekit-1.0.0rc2/examples/tensile/04-results/scripts/paper.py +9 -0
  43. samplekit-1.0.0rc2/examples/tensile/04-results/scripts/read.py +17 -0
  44. samplekit-1.0.0rc2/examples/tensile/04-results/scripts/remeasure.py +8 -0
  45. samplekit-1.0.0rc2/examples/tensile/04-results/scripts/report.py +8 -0
  46. samplekit-1.0.0rc2/examples/tensile/04-results/scripts/selecting.py +9 -0
  47. samplekit-1.0.0rc2/examples/tensile/05-projects/.samplekitrc +30 -0
  48. samplekit-1.0.0rc2/examples/tensile/05-projects/README.md +13 -0
  49. samplekit-1.0.0rc2/examples/tensile/05-projects/cast/.samplekitrc +6 -0
  50. samplekit-1.0.0rc2/examples/tensile/05-projects/cast/samples/aluminium-c1.md +94 -0
  51. samplekit-1.0.0rc2/examples/tensile/05-projects/cast/samples/iron-c1.md +97 -0
  52. samplekit-1.0.0rc2/examples/tensile/05-projects/model/specimen.py +169 -0
  53. samplekit-1.0.0rc2/examples/tensile/05-projects/rolled/.samplekitrc +6 -0
  54. samplekit-1.0.0rc2/examples/tensile/05-projects/rolled/samples/aluminium-01.md +97 -0
  55. samplekit-1.0.0rc2/examples/tensile/05-projects/rolled/samples/copper-01.md +106 -0
  56. samplekit-1.0.0rc2/examples/tensile/05-projects/rolled/samples/steel-01.md +100 -0
  57. samplekit-1.0.0rc2/examples/tensile/05-projects/scripts/projects.py +7 -0
  58. samplekit-1.0.0rc2/examples/tensile/README.md +36 -0
  59. samplekit-1.0.0rc2/pyproject.toml +38 -0
  60. samplekit-1.0.0rc2/python/samplekit/__init__.py +405 -0
  61. samplekit-1.0.0rc2/python/samplekit/__init__.pyi +2218 -0
  62. samplekit-1.0.0rc2/python/samplekit/_figure.py +173 -0
  63. samplekit-1.0.0rc2/python/samplekit/_figures.py +2066 -0
  64. samplekit-1.0.0rc2/python/samplekit/_script.py +231 -0
  65. samplekit-1.0.0rc2/python/samplekit/_scripts.py +212 -0
  66. samplekit-1.0.0rc2/python/samplekit/_worker.py +1458 -0
  67. samplekit-1.0.0rc2/python/samplekit/py.typed +0 -0
  68. samplekit-1.0.0rc2/src/collection/computation.rs +841 -0
  69. samplekit-1.0.0rc2/src/collection/editing.rs +2844 -0
  70. samplekit-1.0.0rc2/src/collection/explanation.rs +671 -0
  71. samplekit-1.0.0rc2/src/collection/exports.rs +1657 -0
  72. samplekit-1.0.0rc2/src/collection/mod.rs +9 -0
  73. samplekit-1.0.0rc2/src/collection/named_queries.rs +147 -0
  74. samplekit-1.0.0rc2/src/collection/renaming.rs +470 -0
  75. samplekit-1.0.0rc2/src/collection/sample_list.rs +1890 -0
  76. samplekit-1.0.0rc2/src/collection/tagging.rs +217 -0
  77. samplekit-1.0.0rc2/src/collection/validation.rs +2787 -0
  78. samplekit-1.0.0rc2/src/config/configuration_edit.rs +597 -0
  79. samplekit-1.0.0rc2/src/config/discovery.rs +1227 -0
  80. samplekit-1.0.0rc2/src/config/mod.rs +7 -0
  81. samplekit-1.0.0rc2/src/config/model_runtime/description.rs +1367 -0
  82. samplekit-1.0.0rc2/src/config/model_runtime.rs +1677 -0
  83. samplekit-1.0.0rc2/src/config/profiles.rs +322 -0
  84. samplekit-1.0.0rc2/src/config/project_config.rs +3641 -0
  85. samplekit-1.0.0rc2/src/config/project_setup.rs +932 -0
  86. samplekit-1.0.0rc2/src/config/project_template/empty/model.py +55 -0
  87. samplekit-1.0.0rc2/src/config/project_template/empty/samplekitrc_existing_model +14 -0
  88. samplekit-1.0.0rc2/src/config/project_template/empty/samplekitrc_head +9 -0
  89. samplekit-1.0.0rc2/src/config/project_template/empty/samplekitrc_model +13 -0
  90. samplekit-1.0.0rc2/src/config/project_template/empty/samplekitrc_no_model +12 -0
  91. samplekit-1.0.0rc2/src/config/project_template/empty/samplekitrc_rest +26 -0
  92. samplekit-1.0.0rc2/src/config/project_template/helpers__init__.py +14 -0
  93. samplekit-1.0.0rc2/src/config/project_template/helpers_measuring.py +26 -0
  94. samplekit-1.0.0rc2/src/config/project_template/sample.md +42 -0
  95. samplekit-1.0.0rc2/src/config/project_template/samplekitrc +194 -0
  96. samplekit-1.0.0rc2/src/config/project_template/specimen.py +164 -0
  97. samplekit-1.0.0rc2/src/config/version_control.rs +4065 -0
  98. samplekit-1.0.0rc2/src/core/dependency_graph.rs +294 -0
  99. samplekit-1.0.0rc2/src/core/formatting.rs +837 -0
  100. samplekit-1.0.0rc2/src/core/identifier.rs +308 -0
  101. samplekit-1.0.0rc2/src/core/link.rs +189 -0
  102. samplekit-1.0.0rc2/src/core/mod.rs +10 -0
  103. samplekit-1.0.0rc2/src/core/property.rs +1804 -0
  104. samplekit-1.0.0rc2/src/core/sample.rs +1824 -0
  105. samplekit-1.0.0rc2/src/core/statistics.rs +187 -0
  106. samplekit-1.0.0rc2/src/core/table.rs +2363 -0
  107. samplekit-1.0.0rc2/src/core/uncertainty.rs +160 -0
  108. samplekit-1.0.0rc2/src/core/value.rs +691 -0
  109. samplekit-1.0.0rc2/src/format/canonicalization.rs +661 -0
  110. samplekit-1.0.0rc2/src/format/document.rs +1752 -0
  111. samplekit-1.0.0rc2/src/format/fingerprint.rs +2006 -0
  112. samplekit-1.0.0rc2/src/format/lock.rs +269 -0
  113. samplekit-1.0.0rc2/src/format/mod.rs +5 -0
  114. samplekit-1.0.0rc2/src/format/schema.rs +1539 -0
  115. samplekit-1.0.0rc2/src/interfaces/cli/completion.rs +754 -0
  116. samplekit-1.0.0rc2/src/interfaces/cli/compute.rs +1513 -0
  117. samplekit-1.0.0rc2/src/interfaces/cli/edit.rs +2786 -0
  118. samplekit-1.0.0rc2/src/interfaces/cli/history.rs +2656 -0
  119. samplekit-1.0.0rc2/src/interfaces/cli/init.rs +582 -0
  120. samplekit-1.0.0rc2/src/interfaces/cli/inspect.rs +2837 -0
  121. samplekit-1.0.0rc2/src/interfaces/cli/plot.rs +1152 -0
  122. samplekit-1.0.0rc2/src/interfaces/cli/run.rs +425 -0
  123. samplekit-1.0.0rc2/src/interfaces/cli/tables.rs +2660 -0
  124. samplekit-1.0.0rc2/src/interfaces/cli.rs +7588 -0
  125. samplekit-1.0.0rc2/src/lib.rs +41 -0
  126. samplekit-1.0.0rc2/src/presentation/changes.rs +1108 -0
  127. samplekit-1.0.0rc2/src/presentation/export_formats.rs +901 -0
  128. samplekit-1.0.0rc2/src/presentation/links.rs +139 -0
  129. samplekit-1.0.0rc2/src/presentation/mod.rs +8 -0
  130. samplekit-1.0.0rc2/src/presentation/opening.rs +279 -0
  131. samplekit-1.0.0rc2/src/presentation/plotting.rs +323 -0
  132. samplekit-1.0.0rc2/src/presentation/scripting.rs +473 -0
  133. samplekit-1.0.0rc2/src/presentation/summaries.rs +525 -0
  134. samplekit-1.0.0rc2/src/presentation/terminal_rendering.rs +2906 -0
  135. samplekit-1.0.0rc2/src/python/mod.rs +8 -0
  136. samplekit-1.0.0rc2/src/python/pyo3_bridge.rs +1815 -0
  137. samplekit-1.0.0rc2/src/python/python_api.rs +13714 -0
  138. samplekit-1.0.0rc2/src/query/field_addressing.rs +1823 -0
  139. samplekit-1.0.0rc2/src/query/filter_language.rs +2741 -0
  140. samplekit-1.0.0rc2/src/query/mod.rs +3 -0
  141. samplekit-1.0.0rc2/src/query/ordering.rs +266 -0
  142. samplekit-1.0.0rc2/src/tui/mod.rs +11 -0
  143. samplekit-1.0.0rc2/src/tui/model.rs +13685 -0
  144. samplekit-1.0.0rc2/src/tui/run.rs +520 -0
  145. samplekit-1.0.0rc2/src/tui/start.rs +1988 -0
  146. samplekit-1.0.0rc2/src/tui/theme.rs +282 -0
  147. samplekit-1.0.0rc2/src/tui/typing.rs +569 -0
  148. samplekit-1.0.0rc2/src/tui/view.rs +4181 -0
  149. samplekit-0.2.0/LICENSE +0 -8
  150. samplekit-0.2.0/PKG-INFO +0 -423
  151. samplekit-0.2.0/README.md +0 -398
  152. samplekit-0.2.0/pyproject.toml +0 -32
  153. samplekit-0.2.0/samplekit/__init__.py +0 -20
  154. samplekit-0.2.0/samplekit/converters.py +0 -117
  155. samplekit-0.2.0/samplekit/property.py +0 -332
  156. samplekit-0.2.0/samplekit/report.py +0 -214
  157. samplekit-0.2.0/samplekit/sample.py +0 -332
  158. samplekit-0.2.0/samplekit/sample_list.py +0 -190
  159. samplekit-0.2.0/samplekit/table.py +0 -612
  160. samplekit-0.2.0/samplekit.egg-info/PKG-INFO +0 -423
  161. samplekit-0.2.0/samplekit.egg-info/SOURCES.txt +0 -15
  162. samplekit-0.2.0/samplekit.egg-info/dependency_links.txt +0 -1
  163. samplekit-0.2.0/samplekit.egg-info/requires.txt +0 -2
  164. samplekit-0.2.0/samplekit.egg-info/top_level.txt +0 -1
  165. samplekit-0.2.0/setup.cfg +0 -4
@@ -0,0 +1,653 @@
1
+ # Changelog
2
+
3
+ What changed in SampleKit from one version to the next, newest first. Each
4
+ version says what was added, changed, removed and fixed, for someone who uses
5
+ SampleKit rather than works on it.
6
+
7
+ A version ending in `-rc.N` is a release candidate: tried before the version
8
+ of that number is published, and replaced by it.
9
+
10
+ ## [1.0.0-rc.2] — 2026-11-04
11
+
12
+ The second release candidate of SampleKit 1.0 (`1.0.0rc2` for Python).
13
+
14
+ ### Upgrading from 1.0.0-rc.1
15
+
16
+ Sample files are unchanged. **`samplekit-migrate.pyz`**, attached to this
17
+ release, makes the renames below in a copy of a project, its samples copied as
18
+ they are. It reads projects of 0.1.0 to 0.2.0 too: their samples, and their
19
+ model's declarations written again, its formulas copied to check by hand. It
20
+ works on a copy and guarantees nothing: back your project up first. [Upgrade from an older
21
+ SampleKit](https://zelyph.github.io/SampleKit/latest/guides/upgrade.html) says
22
+ how to run it, and each step to do by hand; in short:
23
+
24
+ - **Before upgrading, bring every value up to date with rc.1**: `samplekit
25
+ DIR status`, then `samplekit DIR compute --write`. This version does not
26
+ read rc.1's record of which formula computed each value: at its first write
27
+ in a project, it takes the values the files hold as computed by the model
28
+ as it is then, and says how many.
29
+ - **Install the command line and the Python package together**, at the same
30
+ version, on every computer that shares a project.
31
+ - **Add `python = ".venv"` under `[model]`** in `.samplekitrc`, or the path of
32
+ another environment or interpreter: the model runs only in the Python named
33
+ there.
34
+ - **Rename what changed name**: `[workbench.colors]` is `[colors]`, the
35
+ colour role `stale` is `outdated`, a style's `separator` is written as its
36
+ `property`, `{collection}` in a path is `{project}`; in Python, `stale()` and `is_stale` are `outdated()` and
37
+ `is_outdated`. Each old name is refused, naming the new one.
38
+ - `new --like SAMPLE` is `new NAME --keep SAMPLE FIELDS`.
39
+ - **Rename a property's formulas, in this order**: `compute=` becomes
40
+ `compute_value=`, then `compute_quantity=` becomes `compute=`. No value is
41
+ outdated by it. A value formula left as `compute=` still runs, but then owns
42
+ the whole property: an uncertainty typed beside it is no longer kept.
43
+
44
+ ### Cleaning up after rc.1
45
+
46
+ Nothing breaks if you leave these, but they are no longer used:
47
+
48
+ - **In the state folder** (`~/.local/state/samplekit` on Linux,
49
+ `~/Library/Application Support/samplekit` on macOS,
50
+ `%LOCALAPPDATA%\samplekit` on Windows): `computed.json`, `computed`, `consent`, `accepted/`, and the run logs named `logs/compute-YYYY-MM-DDT…Z-PID.log` can be deleted.
51
+ `samplekit-migrate.pyz --clean-state` lists them, and removes them with
52
+ `--write`.
53
+ - **A history shared through git under rc.1** committed files that each
54
+ computer keeps for itself. Remove them from the repository once, then add
55
+ `[history] shared = true` (below): `git rm -r --cached
56
+ .samplekit/history/HEAD .samplekit/history/config
57
+ .samplekit/history/description .samplekit/history/hooks
58
+ .samplekit/model.json`, then `git add -A`.
59
+ - **A value restored from a snapshot rc.1 took** has no record of the formula
60
+ that computed it, and is taken as current: if that formula changed since,
61
+ compute it again, `samplekit DIR compute -p NAME --rerun --write`.
62
+
63
+ ### Added
64
+
65
+ - **A migration tool**, `samplekit-migrate.pyz`, attached to each release: a
66
+ project of 0.1.0, 0.1.1, 0.1.2, 0.2.0 or 1.0.0-rc.1, carried into a new
67
+ folder this version reads, and a report of what was carried, left and to
68
+ check by hand. Run with the Python of an environment where this version's
69
+ package is installed; it installs nothing and never writes in the folder it
70
+ reads.
71
+ - **An export writes a figure**: `[export.curves]` with `figure = "curves"`
72
+ and `output = "paper/curves.pdf"`, where a table's names its `profile`.
73
+ `samplekit export curves --write` then draws it to its file, as a table for
74
+ a paper is written again; Python's `export("curves")` and the TUI's `p`
75
+ menu do the same.
76
+ - **A project's look decides more of its figures**: what `[matplotlib]`
77
+ declares wins over SampleKit's own choices, so `"lines.marker" = "None"`
78
+ draws a `line` figure without markers, and `errorbar.capsize` sets the
79
+ error bars' caps. `style` applies matplotlib's style sheets, by name or as
80
+ `.mplstyle` files, and `fonts` makes font files kept in the project known
81
+ to matplotlib. A figure takes settings of its own: `matplotlib = { … }` in
82
+ `[figure.NAME]`, `@sk.figure(matplotlib={…})` in a model.
83
+ - **`import` takes a file, and a list**: `import = ["..",
84
+ "figures.samplekitrc"]` merges the folder above's `.samplekitrc` and a file
85
+ of any name, a later one over an earlier one, so that a project's settings
86
+ can be kept in files of their own. [Organise a project's
87
+ settings](https://zelyph.github.io/SampleKit/latest/guides/organise-settings.html)
88
+ shows how.
89
+ - **An environment made by uv works**: `samplekit init` installs the package
90
+ into a `.venv/` without pip with `uv pip install`, when uv is on the
91
+ `PATH`, and otherwise says the command to type.
92
+ - **Write a command in any order**: `samplekit samples status`, `samplekit
93
+ status samples` and `samplekit -f 'mass > 10' status samples` are the same,
94
+ and a list of files piped in works, `… | xargs samplekit status`. Where a
95
+ folder has a command's name, the command wins; write the folder `status/`.
96
+ - **Select samples on every command**: `-f` (a filter) and `--query` (a
97
+ filter saved in `.samplekitrc`) now work with `set`, `log`, `diff`,
98
+ `restore`, `validate`, `list`, `explain` and `open` too. `samplekit samples
99
+ set -f 'material == steel' supplier=acme` previews each sample it changes,
100
+ asks once, and writes them together.
101
+ - **Every change is previewed, then asked**: `set`, `new`, `tag`, `export`,
102
+ `restore` and `compute` show what they would change and end on `apply these
103
+ changes? [y/N]`. `--write` answers yes beforehand, for scripts.
104
+ - **Documents in LaTeX, Markdown or HTML**: a style, `[style.NAME]` in
105
+ `.samplekitrc`, says how values, units and tables are written for a kind of
106
+ document. `latex`, `markdown` and `html` are given: `samplekit samples --style
107
+ latex -c name,density -o table.tex` writes a LaTeX table ready to include.
108
+ - **Filters reach into tables**: `results has 42`, a sample with 42 in any cell
109
+ of its `results` table; `results.status has failed`, in one column; and
110
+ `results where (temperature > 100 && status == failed)`, one row meeting
111
+ both. A filter also compares two fields of a sample: `measured >
112
+ expected`.
113
+ - **Scripts in the model**: a method of the model marked `@sk.script` is an
114
+ action on a sample or on a selection, with options; `samplekit run NAME`
115
+ runs it, or `p` in the TUI, and what it changes is previewed before it is
116
+ written.
117
+ - **Share the history through git with one line**: `[history] shared = true`
118
+ in `.samplekitrc`, and `git add -A` takes the history with the samples;
119
+ SampleKit keeps out what each computer makes for itself.
120
+ - **A folder made a project keeps its history**: when a subfolder gets its own
121
+ `.samplekitrc`, the snapshots the project above it took of its files are
122
+ copied into its own history, and this is said.
123
+ - **In the TUI**: a value, a table or a list opens in a window where each cell
124
+ is edited, readings and list items included; `A` adds an attribute to a
125
+ sample, its name then its value; `p` runs a script, draws a figure or writes
126
+ an export from one menu, for the basket, the sample open or the samples
127
+ shown.
128
+ - **Save what a table shows**: `--save query=NAME`, `profile=NAME` or
129
+ `export=NAME` on a table declares its filter, its columns, sort and groups,
130
+ or both and an export, in `.samplekitrc`, previewed first; the TUI's `W`
131
+ does the same.
132
+ - **Remove a sample**: `samplekit FILE remove` deletes sample files, previewed
133
+ first; the history keeps them, and `restore` gives them back. A folder alone
134
+ is refused. In Python, `sample.remove()`.
135
+ - **The note from the command line**: `set note=…` replaces the text below
136
+ the frontmatter, and `note=` empties it.
137
+ - **Links**: an attribute written `"[[steel-02]]"`, as Obsidian writes a
138
+ link, names another sample of the project; one written as Markdown does,
139
+ `"[certificate](../certificates/c.pdf)"`, a file by its path, a sample of
140
+ any project or any other file; one written `https://…` a web page. A list
141
+ of links names several. The TUI opens each with `Enter` or `o`, a sample
142
+ even where the filter leaves it out, its title bar saying the way taken
143
+ and `Esc` going back along it; Python reads the samples with
144
+ `sample.linked("reference")`, a file that is no sample as its path; and
145
+ `validate` reports a link that leads nowhere. A link is shown as Markdown
146
+ shows it, its text alone, underlined in the colour role `link`, `↗` after a
147
+ web page and `✗` after one leading nowhere, in the TUI, and in a table and
148
+ `view` drawn in a terminal; typed, written to a file or a pipe, it is the
149
+ text written. Unquoted, a link is refused at its line, saying to quote it.
150
+ A sample's screen lists the samples linking to it, and the filter `links to
151
+ NAME` selects the samples whose links name it; `set parents=[[a]],[[b]]` writes a list of links, and
152
+ Tab after `[[` completes a sample's name, on `set` and in the TUI.
153
+ - **Rename a sample**: `samplekit FILE rename NEW` renames its file, its own
154
+ files and its `name`, and writes every link of the project leading to it
155
+ again, a link to one of its own files too, previewed first; in the TUI, `R`;
156
+ in Python, `sample.rename(new)`. `remove` lists the links it leaves leading
157
+ nowhere.
158
+ - **A sample's own table written to a file**: `samplekit FILE view curve`
159
+ shows the table `curve` alone, and `--csv`, `--tsv`, `--json` or `-o
160
+ curve.csv` write its rows, previewed and kept in the history as a table of
161
+ samples is; over several samples, a column `name` first. In Python,
162
+ `sample.curve.to_csv()`.
163
+ - **A script may ask with `input()`**: on the command line in the terminal,
164
+ in the TUI in the script's window, which shows what it prints as it prints
165
+ it. The preview lists the answers, and the history keeps them with the
166
+ command.
167
+ - **The TUI does what the command line does**: `C` previews what it would
168
+ compute, as `compute` does; in `H`, `r` puts files back as a change left
169
+ them, `d` shows what a change changed and, on another, compares the two; `t` renames a tag, `old=new`; `N` takes a
170
+ new sample's values, `field=value`, and fields kept from another,
171
+ `keep:FIELD,…`; `D` removes a sample.
172
+ - **An editor of its own window leaves the TUI drawn**: with Notepad or
173
+ `code --wait`, `E` and `M` say what they wait for over the TUI; an editor
174
+ in the terminal takes it as before.
175
+ - **Windows**: tab completion in PowerShell completes the word after the
176
+ cursor, where each Tab wrote the word before it again; `E` and `M` open
177
+ the file in Notepad where no editor is named, rather than in the
178
+ application Windows gives it (Python, for a model); a sample's folder is
179
+ shown in Explorer, its file selected; a lock or a journal a killed command
180
+ left is taken back; paths are said with `\`, never with both separators in
181
+ one message.
182
+ - `new NAME --keep SAMPLE FIELDS` copies the fields named from another
183
+ sample, and nothing else; `[property.NAME] range = [MIN, MAX]` makes a value
184
+ outside the range a defect for `validate`; `compute --timeout SECONDS`
185
+ stops a formula that runs too long and computes the others.
186
+
187
+ ### Changed
188
+
189
+ - **The demo is a tensile test**: three metal specimens in five steps, from
190
+ their sizes written by hand to their curves, results, a paper's tables and
191
+ figures, and two projects read together; in its results, two specimens
192
+ link to the one they are compared with. The start page writes it, and
193
+ *Reset the demo* writes it again as it came.
194
+ - **The documentation is in three parts**: guides, a task each; concepts, how
195
+ SampleKit works and why; and the reference. The home page walks through the
196
+ demo, and a page at an address of rc.1 sends its reader on. New guides
197
+ install SampleKit, start a project, edit, find and link samples, work in
198
+ the terminal interface and from Python, organise settings, read several
199
+ projects together, and say what to do when something goes wrong.
200
+ - **`note` is a reserved name**: a file holding an attribute `note:` is
201
+ refused, saying to rename it `remarks:` or `comment:`; `set note=` and
202
+ `sample.note` are the text below the frontmatter.
203
+ - **A number typed is previewed as the table shows it**: `set area=25.0`
204
+ beside an uncertainty of `0.028` previews `25.000`, and `25.0123` keeps
205
+ every digit typed.
206
+ - **A new row makes what reads its column outdated**: after `set --add-row`,
207
+ a value reading a computed column is *outdated*, as are the values reading
208
+ it, where it was said to wait for the new cell.
209
+ - **`compute` counts unchanged values by cells**, as it counts what it
210
+ computed: `computed 61 values in 3 samples, all unchanged`.
211
+
212
+ - **An uncertainty is rounded up, never understated**: at `.3f`, an
213
+ uncertainty of 0.00006 is written `0.001`, and 0.121 at `.2f` is `0.13`,
214
+ where rc.1 wrote `0.000` and `0.12`. A value is still rounded to the
215
+ nearest. The screen, Python and every file write the same digits, and a
216
+ standard error in a summary is rounded up too. An uncertainty is never
217
+ written as zero, so `validate` and an export no longer warn of one.
218
+ - **A property's formulas are `compute`, `compute_value` and
219
+ `compute_uncertainty`**: `compute=f` gives the whole property, `f()`
220
+ returning a number or `(value, uncertainty)`; `compute_value=` the value
221
+ alone, beside an uncertainty given apart. They were `compute_quantity`,
222
+ `compute` and `compute_uncertainty`.
223
+ - **A table's columns are declared with `sk.Property`**, its formula with
224
+ them: `sk.Property(unit="MPa", compute=self._stress,
225
+ depends_on=["row.force", "area"])`, the function given each row and
226
+ returning its cell, or `compute_column=f`, given the columns it reads and
227
+ returning one cell per row; `compute_value` and `compute_uncertainty` give
228
+ one number of each cell, the other entered. `sk.Column` is gone. A formula filling several
229
+ columns at once is a `sk.ComputedColumns`, in the table's `computed=`;
230
+ `compute_rows` and `compute_columns` are gone. A cell a formula gives is a
231
+ number or `(value, uncertainty)`, as a property's is, where the pair was
232
+ refused.
233
+ - **A project reads its subfolders by default**: `[collection] recursive` is
234
+ `true` unless `.samplekitrc` says `false`, so that `samplekit .` at a
235
+ project's root reads `samples/`. A folder outside any project is read alone,
236
+ as before. A folder reading no sample says so, naming the folders below that
237
+ hold some.
238
+ - **A table names its index**: `sk.Table(columns, index="day")`, or
239
+ `index=["day", "hour"]`; the index given by position is refused.
240
+ - **The command line is in colour on a terminal**, with the TUI's colours: a
241
+ value bold, an attribute and a unit each in its colour, a header bold, a
242
+ state as the TUI shows it, `✓` and `written:` in green. One palette,
243
+ `[colors]`, sets both; a file and a pipe get none.
244
+ - **Numbers in a column line up**: a column of numbers is written to the
245
+ decimals of its most precise, `2.00` beside `1.99`; a column of values with
246
+ uncertainties to one number of decimals where every uncertainty can keep one
247
+ or two significant digits (two when the first is a 1), `12.503 ± 0.004`
248
+ above `12.513 ± 0.010`. A summary's median, minimum and maximum follow
249
+ their column, and `set` says the mean of new readings to the place of their
250
+ uncertainty. A declared precision still wins.
251
+ - **The record of which formula computed each value travels with the
252
+ project**, in its history, no longer on your computer: a formula edited on
253
+ one computer is seen changed on every other, and a colleague's
254
+ recomputed values are current on yours.
255
+ - **Reading computes nothing, in Python too**: a value computed from readings,
256
+ the mean of several measurements, say, has no value until it is
257
+ computed, and says *never computed*, rather than computing behind your back.
258
+ - **A file written from values that are not current says so and exits 2**: an
259
+ export or a table written with `-o` names the values that are outdated,
260
+ failed or never computed. In a CSV, a column `NAME_state` beside the
261
+ quantity says which.
262
+ - **Exports write units as the sample files spell them**, `degC` rather than
263
+ the screen's `°C`; `--style plain` gives the screen's forms.
264
+ - **`restore` without `--at` takes back your last change only**, value by
265
+ value, and keeps what was edited by hand or pulled from a colleague since.
266
+ - **A sample file must declare `schema_version: 1`**; a file without it is not
267
+ a sample, as a README is not. A key written twice in a sample is refused
268
+ rather than silently taking the last.
269
+ - **In the TUI**: a value opens selected, so that what you type replaces it
270
+ (an arrow or `End` keeps it to edit); the basket is kept from one session
271
+ to the next; a file changed in another editor is read again by itself.
272
+
273
+ - **Values not current are counted by cells**, on every surface: a table's
274
+ column outdated in its 13 rows counts 13, and a value and its uncertainty
275
+ given by two formulas count two.
276
+ - **What reads a waiting value waits too**, said *waits for area, which
277
+ waits*, rather than outdated or failed. A value the model no longer
278
+ computes is listed apart, to keep or remove.
279
+ - **One wording for nothing done**: a command with nothing to change says
280
+ `nothing to change — WHY`, and a question answered no says `nothing
281
+ written`.
282
+ - **`validate` reads each sample with the model**, and says where the model
283
+ cannot read one.
284
+ - **A file written from values not current says so on the standard output**,
285
+ so that `-q` or a redirected error stream does not hide it; `--accept
286
+ waiting` is refused, since a waiting value fails nothing.
287
+ - **Over several projects, a snapshot's number is one project's**: `log
288
+ --at`, `diff --from` and `--to` from a folder above several projects are
289
+ refused, naming the project to choose.
290
+ - **The migration tool keeps every sample's note**, 0.x's included.
291
+
292
+ ### Removed
293
+
294
+ - **Reading what earlier versions wrote**: sample files, configuration keys,
295
+ command-line words and Python names of SampleKit 0.2 and rc.1 are refused
296
+ like any unknown one, naming the nearest valid name. The upgrade guide says
297
+ how to carry each over.
298
+ - `new --like`, `compute --try`, and the folder after `explain FILE`: a file
299
+ SampleKit wrote is now found in the projects around it.
300
+
301
+ ### Fixed
302
+
303
+ Many defects found by testing rc.1, among them values reported as current
304
+ that were not, edits lost when two changes met, and a colleague's work pulled
305
+ through git that the history took for your own.
306
+
307
+ ## [1.0.0-rc.1] — 2026-09-28
308
+
309
+ The first release candidate of SampleKit 1.0, and a new SampleKit. Version
310
+ 0.2.0 was a pure Python package for writing samples from a script. 1.0 keeps
311
+ its idea — a sample is a Markdown file, with its data at the top — and is
312
+ built around a command line and a full-screen terminal interface, written in
313
+ Rust, with a Python package beside them for your formulas and your scripts.
314
+ Both come from one source and carry one version (`1.0.0rc1` for Python).
315
+
316
+ It does not read the files 0.2.0 wrote, and no command converts them: see
317
+ [Upgrading from 0.2.0](#upgrading-from-020) below.
318
+
319
+ ### Added
320
+
321
+ **The command line**, `samplekit`, a program with no Python inside. Reading,
322
+ selecting, showing, checking and exporting samples never start Python.
323
+
324
+ - `samplekit FOLDER` prints the path of each sample; with `-c` it shows a
325
+ table of the columns you name, and with `--profile` a table declared in
326
+ `.samplekitrc`. `-f` filters (`-f 'og > 1.050 && tags has medal'`), `-s`
327
+ sorts, `--group` makes a table per value of a field, `--summary` summarises
328
+ each column. `--csv`, `--tsv` and `--json` print the same table as data,
329
+ and `-o FILE --write` writes it.
330
+ - `list` enumerates what a collection holds and declares: its fields, tags,
331
+ queries, profiles, exports, figures, a sample's own files, and the files it
332
+ skipped and why.
333
+ - `view` shows a sample whole: its values, then its tables, and its note with
334
+ `--note`.
335
+ - `status` lists every computed value that is not current, and why;
336
+ `status --exit-code` makes it a check for continuous integration.
337
+ - `compute` runs the model's formulas: alone it says what would run, with
338
+ `--try` it computes and shows the results without writing, and with
339
+ `--write` it writes each sample as soon as it is done. `explain FILE FIELD`
340
+ says where one value came from, and prints a formula's error.
341
+ - `validate` reports the defects of sample files and changes nothing.
342
+ - `new` creates a sample from what the project already knows, `set` changes
343
+ its values (`set brew.md og=1.052 og.u=0.001`, readings, table cells, the
344
+ name), and `tag add`, `tag remove` and `tag rename` change its tags. Each
345
+ shows what it would change and writes only with `--write`.
346
+ - `export NAME` writes a table declared in `.samplekitrc` as CSV, TSV or
347
+ JSON, after a preview; `--status` adds a column holding each row's state.
348
+ - `plot` draws a figure with matplotlib, in a window or to a file: one
349
+ declared in `.samplekitrc` or by the model, or one given by its axes
350
+ (`-x`, `-y`), over properties, attributes or a table's columns.
351
+ - `open` opens a sample's own files — photos, reports, raw data — which
352
+ `[collection] files` says where to find.
353
+ - `init` sets a project up: its `.samplekitrc`, a model to fill in or the
354
+ one you name with `--model PATH`, and a Python environment, `.venv/`, with
355
+ the SampleKit package installed in it. At a terminal it asks; `--example`
356
+ writes a project that runs, one formula of every kind.
357
+ - `tui` opens the full-screen interface on a folder, and `samplekit` alone,
358
+ in a terminal, opens its start page.
359
+ - `completions` sets up tab completion for bash, zsh, fish, elvish and
360
+ PowerShell.
361
+ - Every command has `--help`, with examples, and `-h` for a short summary.
362
+ Errors name what was asked, what exists instead, and the nearest name. Exit
363
+ codes tell a usage error (1) from a data error (2) and a file the system
364
+ refused (3).
365
+
366
+ **A history of every change.** Before SampleKit writes to a project, it keeps
367
+ what the project held, in `.samplekit/history/`, beside `.samplekitrc`: the
368
+ sample files, the configuration and the model's source. There is nothing to
369
+ set up, and no Git to install.
370
+
371
+ - `log` lists the snapshots, `diff` compares two states value by value
372
+ (`abv 6.8 ± 0.1 % → 7.2 ± 0.1 %`), and `restore` puts files back: the last
373
+ change taken back, or the state of a given snapshot.
374
+ - `--at` reads the whole project as it was at a snapshot or on a date:
375
+ `samplekit brews -c name,abv --at 2026-09-12`. An export made again this
376
+ way is the same file, byte for byte.
377
+ - A change made in your text editor is kept as a snapshot of its own, so it
378
+ is never credited to the command that follows.
379
+ - A Python script's writes are one snapshot, with the script's source: `log
380
+ --script N` prints the script as it ran, and `keep()` marks a snapshot of
381
+ its own from inside a script.
382
+ - Every export and figure SampleKit writes is recorded. `explain FILE` finds
383
+ it again, even copied into a manuscript's folder and renamed: what made it,
384
+ from which samples, whether it is still current, and the command that makes
385
+ it again.
386
+ - `log --export` writes the history as Markdown, to share beside the samples.
387
+ - One history over several computers: each writes its own branch in
388
+ `.samplekit/history/`, and the next snapshot joins another computer's once
389
+ your files hold its state — no command merges. `log` shows a `machine`
390
+ column where more than one wrote, and an undo takes back only this
391
+ computer's change. A synchronised folder shares it as it is; git, once the
392
+ history's own `.gitignore` stops ignoring everything.
393
+
394
+ **Current and outdated values.** Every computed value carries a short record
395
+ of what it was computed from. SampleKit compares it with the file as it is
396
+ now, and knows — from the files alone, without running Python — which values
397
+ are *outdated* (an input changed), *edited* (typed by hand over the formula),
398
+ *failed* (the formula raised an error) or *never computed*. Your computer also
399
+ keeps a digest of each formula, so that editing a formula makes outdated only
400
+ the values it computed, and a comment or a docstring changes nothing. A value
401
+ whose input nobody entered yet *waits* for it, and is not an error.
402
+
403
+ - The states appear in the same words everywhere: `status`, marks in tables
404
+ (`⚠` outdated, `✎` edited, `✗` failed), the TUI, Python. `state` is also a
405
+ field: `-f 'state == outdated'`, `-c name,state`.
406
+ - A value typed over its formula is an **override**: `compute` leaves it
407
+ alone until `compute --force` gives it back to its formula.
408
+ - A formula that fails does not stop the run: the other values are computed,
409
+ the file records the error's type, and the full traceback is kept for
410
+ `explain`. An interrupted run loses only the sample in progress.
411
+
412
+ **The full-screen interface, the TUI**, for a project you work in every day.
413
+
414
+ - A **start page**: recent projects, a new project set up by a few questions,
415
+ a folder opened or configured, and the guide and demo.
416
+ - **The collection**: filter, sort, group, choose columns, summarise, and
417
+ save what it shows as a query, a profile or an export of the project.
418
+ - **A sample**: its values, tables, readings and note, each value editable;
419
+ its own files opened; a value computed, or everything not current.
420
+ - **The history**: the snapshots, what each changed, and `u` to take the last
421
+ change back.
422
+ - **The configuration** edited from the TUI, and the colours set by role in
423
+ `[tui.colors]`.
424
+ - **Editing as in a text editor**, wherever you type — the filter, a value,
425
+ a name, a setting, the note: the mouse selects, by word on a double click;
426
+ `Shift` and the arrows select; `Ctrl` and the arrows move by word;
427
+ `Ctrl+Backspace` and `Ctrl+Delete` take a word out; `Ctrl+Z` undoes,
428
+ `Ctrl+Shift+Z` redoes; `Ctrl+C` copies a selection, `Ctrl+X` cuts, and a
429
+ paste goes where the cursor is. In the note, Markdown by keys: `*`, `_` or
430
+ `~` around a selection, `Alt+1` to `Alt+6` a heading, `Alt+L` a link,
431
+ `Alt+I` an image, `Alt+C` a code block.
432
+ - `?` lists every key of the screen you are on.
433
+
434
+ **A demo**: a home brewer's notebook in eight steps, from three brews written
435
+ by hand to a model, tables, figures, two projects and Python. The start page
436
+ writes it for you (`g`), and it is also in the source, in `examples/brewing/`.
437
+
438
+ **Several projects in one command.** `samplekit status ana/brews tom/brews`
439
+ reads both; each sample keeps its own project's units, precisions, model,
440
+ queries and profiles, and the terminal shows one table per project.
441
+
442
+ **New in the sample file** (the format itself is under *Changed*):
443
+
444
+ - **`n/a`**, not applicable: a value that does not apply to this sample, as
445
+ distinct from one nobody has measured yet. A formula reading it gives `n/a`
446
+ too, without running.
447
+ - **Attributes**: facts about a sample — a style, a supplier, a date, a list
448
+ of hops — with no unit and no uncertainty, filtered and shown like
449
+ properties.
450
+ - **Tags**, a deliberate label on a sample (`tags: [medal]`), reached by a
451
+ filter (`tags has medal`).
452
+ - **A sample's own files** — photos, reports, raw data — found by the
453
+ sample's name where `[collection] files` says, and opened from the command
454
+ line, the TUI or Python.
455
+
456
+ **A project's configuration**, `.samplekitrc`, in the project's folder: the
457
+ model; queries, profiles, exports and figures by name; how a unit is shown in
458
+ a table, in LaTeX and on a figure (`[unit.*]`); the precision, symbol and unit
459
+ of each quantity, declared once (`[property.*]`); matplotlib's settings;
460
+ which files are samples. Every key is checked: a misspelt one is an error
461
+ that names the line and the nearest valid key.
462
+
463
+ - **`import = ".."`** takes the configuration of the folder named, merged key
464
+ by key beneath this one: what several collections share is declared once,
465
+ at their common root, and each collection keeps its model and what is its
466
+ own. Imports chain; a loop is refused. A relative path reads from the file
467
+ that declares it, and `{collection}` names a sample's collection folder in
468
+ `[collection] files` and an export's `output`.
469
+ - **What the current folder offers**: `list profiles`, `queries`, `exports`
470
+ and `figures`, the completion and the TUI's `f` show the configuration of
471
+ the folder a command runs from, with what it imports, each name marked
472
+ `local` or with the file it comes from. A sample still follows its own
473
+ collection's configuration wherever the command runs from.
474
+ - `validate` notes a declaration identical to the one imported, a copy to
475
+ remove, and `--show-rc` names the files imported.
476
+
477
+ **One warning a command.** A command that would warn several times of one
478
+ thing — samples a profile or an export several projects lack, targets that
479
+ are not Markdown, histories not kept, what a model logged — says it once, in
480
+ one line counting them and ending *-v shows them*; `-v` lists each.
481
+
482
+ **The documentation**, in four parts: a tutorial that follows the demo;
483
+ explanations, a page per notion; how-to guides for a task you have in mind;
484
+ and a reference — every command and option, the Python API, the TUI's keys,
485
+ every key of `.samplekitrc`, the sample file format, the exit codes.
486
+
487
+ ### Changed
488
+
489
+ **The note below the data is yours.** 0.2.0 wrote the body of a file from the
490
+ model's `template()` at every save. SampleKit now never writes, reformats or
491
+ reads the note: not a character, not a line ending. Tables for a reader are
492
+ what the command line, the TUI and exports show.
493
+
494
+ **The sample file.** A sample is still one Markdown file with its data in a
495
+ YAML block at the top, written differently:
496
+
497
+ ```yaml
498
+ ---
499
+ schema_version: 1
500
+ style: stout
501
+ properties:
502
+ og: {v: 1.058, u: 0.001, readings: [1.057, 1.058, 1.059]}
503
+ volume: {v: 19.5, unit: L}
504
+ tables:
505
+ fermentation:
506
+ index: day
507
+ columns:
508
+ day: {unit: d}
509
+ gravity: {}
510
+ rows:
511
+ - {day: 1, gravity: 1.041}
512
+ ---
513
+ ```
514
+
515
+ - `schema_version: 1` heads the file. Quantities go under `properties:`,
516
+ tables under `tables:`, and any other top-level key is an attribute.
517
+ - A quantity is a bare number or a short mapping: `v` for the value (was
518
+ `value`), `u` for the standard uncertainty in the value's unit (was
519
+ `uncertainty`), `readings` for the repeated measurements (was `data`), and
520
+ `unit`. `og.v` and `og.u` are also how a filter, a column, `set` and Python
521
+ name the two numbers.
522
+ - A table has `index`, `columns` as a mapping and `rows` (was `_index`,
523
+ `_columns` as a list, `_rows`), an index of one column or several, and
524
+ cells that are quantities. `fermentation.gravity[3]` names the gravity on
525
+ day 3.
526
+ - A precision and a LaTeX spelling are no longer written in each file
527
+ (`precision`, `precision_unc`, `symbol_math`, `unit_math`): they are
528
+ declared once in `.samplekitrc`, where a quantity's unit and symbol can be
529
+ too. A formula is never written in a file.
530
+ - `name:` is optional: without it, the file's name without its extension
531
+ stands for the sample's name.
532
+ - A computed value carries a short record of what it was computed from
533
+ (`computed:`, `fingerprint:`), which is how SampleKit tells it is current.
534
+ - Names of properties, attributes, tables, columns and tags start with a
535
+ letter or `_` and hold letters, digits and `_`, in any alphabet: `température` and
536
+ `bière` are names, `dry-hopped` is not.
537
+ - The data is written in one fixed form: the first write to a file written
538
+ by hand may change more lines than the value, and after that only what
539
+ changed. A write goes to a temporary file first, and is refused if the file
540
+ changed on disk since it was read.
541
+
542
+ **Readings and their statistic.** In 0.2.0, a list of measurements gave the
543
+ mean and the sample standard deviation on its own. The value of readings is
544
+ now **the statistic the model declares** — mean, median, minimum, maximum or
545
+ a quartile, and standard error or a standard deviation for the uncertainty —
546
+ or a value written beside them. Readings with neither have no value, and
547
+ every surface says so rather than taking a mean nobody chose. The file notes
548
+ which statistic gave the numbers (`statistics: {v: mean, u: standard_error}`).
549
+
550
+ **Units, symbols and precision** are the project's. A unit is a label, never
551
+ converted; `[unit."degC"]` says how it is shown in a table (`°C`), in LaTeX
552
+ and on a figure. Two samples writing different units for one quantity are
553
+ reported rather than put side by side. A declared precision rounds alike on
554
+ the screen and in an export.
555
+
556
+ **The model and computing.** A project names its model, a subclass of
557
+ `samplekit.Sample`, in `.samplekitrc` (`[model] path` and `class`), and every
558
+ tool uses it. Each computed value has a formula of its own and names what it
559
+ reads:
560
+
561
+ ```python
562
+ self.abv = sk.Property(unit="%", compute_quantity=self._abv, depends_on=["og", "fg"])
563
+ ```
564
+
565
+ - `compute=` gives the value, `compute_uncertainty=` (was `compute_unc`) the
566
+ uncertainty beside a value you enter, and `compute_quantity=` both. A
567
+ table's cells are computed a row at a time (`compute_rows`) or a column at
568
+ once (`compute_columns`).
569
+ - `depends_on` names the inputs by name (`"og"`, `"fermentation.gravity"`),
570
+ not as `Property` objects, and it is required: a model with a formula
571
+ that declares nothing is refused before any formula runs.
572
+ - **A model declares no symbol and no precision**: both are the project's,
573
+ in `[property.*]`, as the unit's display is. `sk.Property` and `sk.Column`
574
+ refuse `symbol=` and `precision=`, saying where each is declared.
575
+ - **Nothing is computed when a value is read.** Values are computed when you
576
+ ask — `samplekit compute`, `C` or `c` in the TUI, `compute()` in Python —
577
+ and only what is not current, in the order the inputs require. A change to
578
+ an input marks what reads it as outdated; it does not recompute it.
579
+ - **A value set over a formula is an override**: the formula stays in the
580
+ model, and the value is marked edited, where 0.2.0 dropped the formula.
581
+ - The command line runs the model in the Python of the nearest `.venv/`
582
+ above the samples, or the interpreter `[model] python` names, where the
583
+ SampleKit package must be installed in the same version. It runs without
584
+ asking, as `python` runs a script you give it, and names the configuration
585
+ and the model file before it starts.
586
+ - The model is described once, in `.samplekit/model.json`, written whenever
587
+ it is imported and again when its files change: what only reads the model's
588
+ declarations — `status`, `new` and `set`, a `state` filter, the TUI — reads
589
+ the description and starts no Python. Python runs to compute and to draw.
590
+ - What a model prints goes to a log file; its warnings are shown during the
591
+ run.
592
+ - Uncertainties are never propagated on their own: a formula that should give
593
+ one returns it.
594
+
595
+ **The Python package** is built from the same Rust code as the command line,
596
+ and changes its shape:
597
+
598
+ - `sk.load(path)` reads a sample or a folder, with the project's model
599
+ (was `SampleList(path, sample_class=…)` and `Sample.load`); `sk.load(a, b)`
600
+ reads several folders into one list.
601
+ - `Sample.new(path)` makes a sample, `save()` writes it, `compute()` computes
602
+ what is not current, and `not_current()` lists what `status` would.
603
+ - A `SampleList` filters with the command line's language
604
+ (`brews.filter("og > 1.050")`) as well as a function, sorts, groups
605
+ (`group_by`), selects with a query of the project, and writes CSV, TSV or
606
+ JSON at the project's precisions (`to_csv`, `to_tsv`, `to_json`,
607
+ `to_dict`).
608
+ - `sk.stats` names the statistics readings can stand for, and
609
+ `sk.plot` draws the same figures as the command line.
610
+ - Reading a value that is not current returns it, with a warning saying why.
611
+ - The package ships type stubs that carry its documentation, so that an
612
+ editor shows both.
613
+ - Python 3.11 or newer is required (was 3.10), and neither PyYAML nor pandas
614
+ is needed.
615
+
616
+ **Performance.** The package is built in Rust. Reading and writing sample
617
+ files is 25 to 35 times faster than 0.2.0, and the statistics of readings
618
+ about 3 times faster. A value computed costs a little more — some 15 µs per
619
+ formula run — for the record of what it read, which is how SampleKit knows
620
+ afterwards that it is still current; beside a formula that takes seconds, it
621
+ does not show. `status`, `new`, `set` and the TUI start no Python while the
622
+ model's description is current.
623
+
624
+ ### Removed
625
+
626
+ - **Reading 0.2.0 files.** Every command names such a file, says why, reads
627
+ the rest and exits with code 2.
628
+ - **The long spellings `value:` and `uncertainty:`.** They are not read, in a
629
+ property, a table's cell or anywhere else in a sample file: such a file is
630
+ not read, and every command names the key and what it is written now —
631
+ `'value' is written 'v' since schema 1: rename it`.
632
+ - **`template()` and the `report` module**: the note is no longer generated.
633
+ Tables for a paper are profiles and exports; figures are drawn with `plot`.
634
+ - **pandas**: `to_dataframe()` and the `converters` module; `stats()` on a
635
+ list gives a `Summary` rather than a DataFrame. `to_csv()` or `to_dict()`
636
+ give data pandas reads, and `--summary`, `Summary` and `sk.stats` summarise.
637
+ - **Computing on read**, and the cache a changed input cleared.
638
+ - **The automatic mean and standard deviation of a list**: a statistic is
639
+ declared.
640
+
641
+ ### Upgrading from 0.2.0
642
+
643
+ No command converts a 0.2.0 sample: the model has to be rewritten by hand
644
+ anyway, and a tool that did half the work would take all the risk of writing
645
+ to your files. [Upgrade from an older SampleKit](docs/guides/upgrade.md)
646
+ shows how to carry each sample across — its values, uncertainties, readings,
647
+ tables and text — with both versions installed side by side, into a new
648
+ folder, and how to check the result.
649
+
650
+ ## [0.2.0]
651
+
652
+ The previous SampleKit, a Python package published on the Python Package
653
+ Index. Its changes are not recorded here.