simaudit 0.1.0a1__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.
- simaudit-0.1.0a1/CHANGELOG.md +15 -0
- simaudit-0.1.0a1/CITATION.cff +37 -0
- simaudit-0.1.0a1/LICENSE +21 -0
- simaudit-0.1.0a1/MANIFEST.in +6 -0
- simaudit-0.1.0a1/PKG-INFO +230 -0
- simaudit-0.1.0a1/README.md +194 -0
- simaudit-0.1.0a1/THIRD_PARTY.md +32 -0
- simaudit-0.1.0a1/VALIDATION.md +118 -0
- simaudit-0.1.0a1/docs/ADAPTERS.md +70 -0
- simaudit-0.1.0a1/docs/REPORTS.md +32 -0
- simaudit-0.1.0a1/docs/REQUIREMENTS.md +112 -0
- simaudit-0.1.0a1/examples/cartpole-probes.json +1 -0
- simaudit-0.1.0a1/examples/custom_simulator.py +24 -0
- simaudit-0.1.0a1/examples/probes.json +1 -0
- simaudit-0.1.0a1/pyproject.toml +53 -0
- simaudit-0.1.0a1/references.bib +17 -0
- simaudit-0.1.0a1/setup.cfg +4 -0
- simaudit-0.1.0a1/src/simaudit/__init__.py +9 -0
- simaudit-0.1.0a1/src/simaudit/__main__.py +2 -0
- simaudit-0.1.0a1/src/simaudit/adapters/__init__.py +3 -0
- simaudit-0.1.0a1/src/simaudit/adapters/base.py +68 -0
- simaudit-0.1.0a1/src/simaudit/adapters/cartpole.py +66 -0
- simaudit-0.1.0a1/src/simaudit/adapters/mujoco.py +108 -0
- simaudit-0.1.0a1/src/simaudit/checks.py +220 -0
- simaudit-0.1.0a1/src/simaudit/cli.py +53 -0
- simaudit-0.1.0a1/src/simaudit/pytest_plugin.py +43 -0
- simaudit-0.1.0a1/src/simaudit/release.py +107 -0
- simaudit-0.1.0a1/src/simaudit/report.py +151 -0
- simaudit-0.1.0a1/src/simaudit/worker.py +15 -0
- simaudit-0.1.0a1/src/simaudit.egg-info/PKG-INFO +230 -0
- simaudit-0.1.0a1/src/simaudit.egg-info/SOURCES.txt +43 -0
- simaudit-0.1.0a1/src/simaudit.egg-info/dependency_links.txt +1 -0
- simaudit-0.1.0a1/src/simaudit.egg-info/entry_points.txt +5 -0
- simaudit-0.1.0a1/src/simaudit.egg-info/requires.txt +16 -0
- simaudit-0.1.0a1/src/simaudit.egg-info/top_level.txt +1 -0
- simaudit-0.1.0a1/tests/test_checks.py +200 -0
- simaudit-0.1.0a1/tests/test_mujoco.py +22 -0
- simaudit-0.1.0a1/tests/test_plugin.py +24 -0
- simaudit-0.1.0a1/validation/PROTOCOL.md +29 -0
- simaudit-0.1.0a1/validation/known_cases.py +43 -0
- simaudit-0.1.0a1/validation/make_receipt.py +17 -0
- simaudit-0.1.0a1/validation/run_controls.py +51 -0
- simaudit-0.1.0a1/validation/run_known_cases.py +43 -0
- simaudit-0.1.0a1/validation/run_release_cases.py +19 -0
- simaudit-0.1.0a1/validation/verify_receipt.py +19 -0
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.1.0a1 - 2026-11-14
|
|
4
|
+
|
|
5
|
+
First public alpha.
|
|
6
|
+
|
|
7
|
+
- Checks: parameter-assignment consistency, integrator-order convergence,
|
|
8
|
+
release comparison across interpreters, and observation consistency.
|
|
9
|
+
- Six explicit statuses (PASS, FAIL, ABSTAIN, SKIPPED, PARTIAL, BLOWUP) and
|
|
10
|
+
strict JSON reports with stated limitations.
|
|
11
|
+
- CLI, pytest fixture, Gymnasium CartPole/Reacher adapters, MuJoCo order adapter.
|
|
12
|
+
- Fixed before publication: `compare_releases` exposed the caller's whole
|
|
13
|
+
site-packages to its worker interpreters when pip-installed; workers now see
|
|
14
|
+
only the simaudit package.
|
|
15
|
+
- Validation includes MuJoCo 3.16.0, 3.17.0 and 4.0.1 (see VALIDATION.md).
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
cff-version: 1.2.0
|
|
2
|
+
message: "Please cite the two method papers below when using simaudit. Paper authorship is recorded separately from software authorship."
|
|
3
|
+
title: "simaudit: scoped diagnostics for simulators"
|
|
4
|
+
type: software
|
|
5
|
+
version: 0.1.0a1
|
|
6
|
+
date-released: 2026-11-14
|
|
7
|
+
repository-code: "https://github.com/TesfayZ/simaudit"
|
|
8
|
+
keywords:
|
|
9
|
+
- reinforcement learning
|
|
10
|
+
- simulator validation
|
|
11
|
+
- MuJoCo
|
|
12
|
+
- Gymnasium
|
|
13
|
+
- numerical verification
|
|
14
|
+
abstract: "Scoped, reproducible diagnostics for user-supplied simulators: parameter-assignment consistency, integrator-order convergence, release comparison and observation consistency, with explicit statements of what was not assessed."
|
|
15
|
+
license: MIT
|
|
16
|
+
authors:
|
|
17
|
+
- family-names: Gebrekidan
|
|
18
|
+
given-names: Tesfay Zemuy
|
|
19
|
+
references:
|
|
20
|
+
- type: article
|
|
21
|
+
title: "Diagnosing Model and Discretisation Errors in Reinforcement Learning Simulators Using Invariants"
|
|
22
|
+
authors:
|
|
23
|
+
- family-names: Gebrekidan
|
|
24
|
+
given-names: Tesfay Zemuy
|
|
25
|
+
year: 2026
|
|
26
|
+
doi: 10.5281/zenodo.24114609
|
|
27
|
+
url: "https://zenodo.org/records/24114609"
|
|
28
|
+
- type: article
|
|
29
|
+
title: "Diagnosing Faults in Reinforcement Learning Simulators with Canonical Polynomial Invariants"
|
|
30
|
+
authors:
|
|
31
|
+
- family-names: Gebrekidan
|
|
32
|
+
given-names: Tesfay Zemuy
|
|
33
|
+
- family-names: Gebrerufael
|
|
34
|
+
given-names: Hadush Hailu
|
|
35
|
+
year: 2026
|
|
36
|
+
doi: 10.48550/arXiv.2609.13194
|
|
37
|
+
url: "https://arxiv.org/abs/2609.13194v2"
|
simaudit-0.1.0a1/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Tesfay Zemuy Gebrekidan
|
|
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,230 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: simaudit
|
|
3
|
+
Version: 0.1.0a1
|
|
4
|
+
Summary: Scoped, reproducible diagnostics for user-supplied simulators
|
|
5
|
+
Author: Tesfay Zemuy Gebrekidan
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Repository, https://github.com/TesfayZ/simaudit
|
|
8
|
+
Project-URL: Issues, https://github.com/TesfayZ/simaudit/issues
|
|
9
|
+
Project-URL: Documentation, https://github.com/TesfayZ/simaudit#readme
|
|
10
|
+
Keywords: reinforcement-learning,simulator,verification,mujoco,gymnasium,testing
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Intended Audience :: Science/Research
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
18
|
+
Classifier: Topic :: Scientific/Engineering
|
|
19
|
+
Classifier: Framework :: Pytest
|
|
20
|
+
Requires-Python: >=3.11
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
License-File: LICENSE
|
|
23
|
+
Requires-Dist: numpy>=1.24
|
|
24
|
+
Provides-Extra: gymnasium
|
|
25
|
+
Requires-Dist: gymnasium>=1.0; extra == "gymnasium"
|
|
26
|
+
Provides-Extra: mujoco
|
|
27
|
+
Requires-Dist: mujoco>=3.2; extra == "mujoco"
|
|
28
|
+
Requires-Dist: gymnasium>=1.0; extra == "mujoco"
|
|
29
|
+
Provides-Extra: pytest
|
|
30
|
+
Requires-Dist: pytest>=8; extra == "pytest"
|
|
31
|
+
Provides-Extra: dev
|
|
32
|
+
Requires-Dist: pytest>=8; extra == "dev"
|
|
33
|
+
Requires-Dist: build>=1; extra == "dev"
|
|
34
|
+
Requires-Dist: gymnasium>=1.0; extra == "dev"
|
|
35
|
+
Dynamic: license-file
|
|
36
|
+
|
|
37
|
+
# simaudit
|
|
38
|
+
|
|
39
|
+
Installable diagnostics for **your own simulator**, with explicit limits on what
|
|
40
|
+
was assessed. Supply a small Python adapter; NumPy is the only core dependency.
|
|
41
|
+
Gymnasium and MuJoCo are optional examples. Version **0.1.0a1** is an alpha,
|
|
42
|
+
not a generally calibrated simulator-certification service.
|
|
43
|
+
|
|
44
|
+
## Install
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
# Pre-release: pip skips alpha versions unless you ask for them.
|
|
48
|
+
python -m pip install --pre simaudit
|
|
49
|
+
# Optional adapters and pytest integration:
|
|
50
|
+
python -m pip install --pre 'simaudit[gymnasium,mujoco,pytest]'
|
|
51
|
+
# Or from a source checkout:
|
|
52
|
+
python -m pip install .
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Python 3.11 or newer (CI matrix 3.11-3.14; local validation on 3.14.6 and 3.12.3). Nothing downloads, runs experiments, or imports an optional
|
|
56
|
+
simulator on package import. For development: `python -m pip install '.[dev]'`.
|
|
57
|
+
|
|
58
|
+
## Test your own simulator
|
|
59
|
+
|
|
60
|
+
```python
|
|
61
|
+
import numpy as np
|
|
62
|
+
from simaudit import Adapter, Feature, Report, check_order
|
|
63
|
+
|
|
64
|
+
# Wrap your simulator so each call restores every dependency of its next step.
|
|
65
|
+
def step(state, action, dt):
|
|
66
|
+
return state + dt * (state + action)
|
|
67
|
+
|
|
68
|
+
def native_reference(state, action, dt):
|
|
69
|
+
return np.asarray([(1 + dt) * state[0] + dt * action])
|
|
70
|
+
|
|
71
|
+
adapter = Adapter(
|
|
72
|
+
name="my simulator", step_fn=step, native_step_fn=native_reference,
|
|
73
|
+
features=[Feature("dynamics", "tested", "Smooth scalar ODE, no hidden state", True)],
|
|
74
|
+
smooth=True, declared_order=1, dt=0.2,
|
|
75
|
+
)
|
|
76
|
+
result = check_order(adapter, [[0.5], [1.0], [1.5]], [0, 0, 0])
|
|
77
|
+
report = Report([result])
|
|
78
|
+
print(report.summary())
|
|
79
|
+
report.write("audit.json")
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
The native reference should call the original simulator without the adapter
|
|
83
|
+
transformation being tested. A user-provided reference and inventory are trusted
|
|
84
|
+
inputs, not automatically verified specifications. See [the adapter guide](docs/ADAPTERS.md)
|
|
85
|
+
for mutable simulators, noise, arbitrary state metrics, assignment factories,
|
|
86
|
+
observation maps and capability requirements.
|
|
87
|
+
|
|
88
|
+
The CLI loads a trusted local `module:function` returning an adapter. From this
|
|
89
|
+
directory, run the included NumPy-only example:
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
PYTHONPATH=examples simaudit order --factory custom_simulator:factory \
|
|
93
|
+
--probes examples/probes.json --strict --json audit.json
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Python factories execute code with your Python process's privileges. Probe files
|
|
97
|
+
contain only `states` and matching `actions`. The same interface works with your
|
|
98
|
+
own importable adapter module. `python -m simaudit` is equivalent to `simaudit`.
|
|
99
|
+
|
|
100
|
+
## Four independent checks
|
|
101
|
+
|
|
102
|
+
| Check | Python API | Needs | What it reports |
|
|
103
|
+
|---|---|---|---|
|
|
104
|
+
| Parameter assignment | `check_assignment` | Replay, native reference, assignment factory | Baseline/direct/fresh comparison; no-op, stale, consistent or unresolved |
|
|
105
|
+
| Numerical order | `check_order` | Replay, native reference, timestep, declared order, smoothness, distance | Stable refinement slopes, below-declaration order, or inability to diagnose |
|
|
106
|
+
| Release/variant comparison | `compare_releases` | Factory and two supplied Python interpreters | First-step differences on identical inputs; later differences recorded separately |
|
|
107
|
+
| Observation consistency | `check_observation` | Replay, native reference, observed and reference observation maps | Differences above the precision/noise floor |
|
|
108
|
+
|
|
109
|
+
The package doesn't implement the papers' energy-fingerprint classifier,
|
|
110
|
+
algebraic localisation, parameter identification or automatic equation diagnosis.
|
|
111
|
+
A numerical-order pass cannot establish that equations are correct.
|
|
112
|
+
|
|
113
|
+
## Results and limitations
|
|
114
|
+
|
|
115
|
+
Every result contains **PASS, FAIL, ABSTAIN, SKIPPED, PARTIAL or BLOWUP**, a reason,
|
|
116
|
+
scope, limitations, mandatory feature coverage, modifications, versions and raw
|
|
117
|
+
probe measurements. Limitations and untested features are printed beside results.
|
|
118
|
+
|
|
119
|
+
- Float32 or a declared noise floor blocks a healthy consistency verdict in
|
|
120
|
+
checks A, B and D. A and D can still flag discrepancies above their floors.
|
|
121
|
+
- Missing capabilities return `SKIPPED`; unsupported smoothness/features give
|
|
122
|
+
`PARTIAL`; unresolved or unstable slopes give `ABSTAIN`.
|
|
123
|
+
- Nonfinite trajectories or detected engine resets give `BLOWUP`.
|
|
124
|
+
- A failed adapter replay/native-step self-test blocks downstream diagnosis.
|
|
125
|
+
- `PASS` covers the supplied probes under the declared conditions only. A wrong
|
|
126
|
+
reference, shared implementation error or unreported hidden state can escape it.
|
|
127
|
+
|
|
128
|
+
By default `FAIL` exits 1; other nonpasses print a warning and exit 0. `--strict`
|
|
129
|
+
exits 1 on **any** nonpass. Configuration errors exit 2. Empty reports are never
|
|
130
|
+
all-clear. [Report contract](docs/REPORTS.md).
|
|
131
|
+
|
|
132
|
+
## Optional adapters and releases
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
simaudit assignment --factory simaudit.adapters.cartpole:cartpole \
|
|
136
|
+
--probes examples/cartpole-probes.json --parameter masscart --value 2 --json cartpole.json
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
This deliberately detects the paper's assignment discrepancy and exits 1. The
|
|
140
|
+
fresh CartPole reference explicitly rebuilds dependent constants; it does not
|
|
141
|
+
assert that Gymnasium promises arbitrary attribute mutation.
|
|
142
|
+
|
|
143
|
+
`mujoco_xml(xml)` supplies native replay for smooth autonomous rigid models and
|
|
144
|
+
rejects unsupported features. `reacher()` supplies a cache-consistency comparison
|
|
145
|
+
against the shipped observation map after `mj_forward`. Both live in
|
|
146
|
+
`simaudit.adapters.mujoco`; imports are lazy. Custom adapters are not subject to
|
|
147
|
+
these built-in models' limitations but must declare their own.
|
|
148
|
+
|
|
149
|
+
For a release pair, create two separate environments yourself, with NumPy and
|
|
150
|
+
the simulator versions you want, then supply their Python paths:
|
|
151
|
+
|
|
152
|
+
```bash
|
|
153
|
+
simaudit release --factory simaudit.adapters.cartpole:cartpole \
|
|
154
|
+
--probes examples/cartpole-probes.json --python-a /path/a/bin/python \
|
|
155
|
+
--python-b /path/b/bin/python --strict --json releases.json
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
The worker runs the current package source in each interpreter. The package does
|
|
159
|
+
not install or upgrade either environment. Your custom factory module must also
|
|
160
|
+
be importable there. Missing versions remain untested. A changed first step means
|
|
161
|
+
compatibility changed, not necessarily that either release is wrong.
|
|
162
|
+
|
|
163
|
+
## pytest and verification
|
|
164
|
+
|
|
165
|
+
Install the `pytest` extra to use the auto-discovered `simaudit_check` fixture:
|
|
166
|
+
|
|
167
|
+
```python
|
|
168
|
+
def test_simulator(simaudit_check):
|
|
169
|
+
simaudit_check(check_order(adapter, [[1.0]], [0]))
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
`FAIL` fails the test; other nonpasses skip with their original status and full
|
|
173
|
+
reason. `pytest --simaudit-strict --simaudit-json results.json` fails the session
|
|
174
|
+
on any nonpass and preserves the separate audit counts. Reporting is serial;
|
|
175
|
+
pytest-xdist aggregation is not supported.
|
|
176
|
+
|
|
177
|
+
Run software tests with `python -m pytest`. Build with
|
|
178
|
+
`python -m build`. See [validation results](VALIDATION.md),
|
|
179
|
+
[prospective paper-case protocol](validation/PROTOCOL.md), and
|
|
180
|
+
[requirements and acceptance criteria](docs/REQUIREMENTS.md). These are small package tests
|
|
181
|
+
and selected reproductions, not a general false-alarm guarantee.
|
|
182
|
+
|
|
183
|
+
## Cite the two method papers
|
|
184
|
+
|
|
185
|
+
1. **Tesfay Zemuy Gebrekidan** (2026). *Diagnosing Model and Discretisation Errors
|
|
186
|
+
in Reinforcement Learning Simulators Using Invariants*.
|
|
187
|
+
[Zenodo](https://zenodo.org/records/24114609).
|
|
188
|
+
2. **Tesfay Zemuy Gebrekidan and Hadush Hailu Gebrerufael** (2026).
|
|
189
|
+
*Diagnosing Faults in Reinforcement Learning Simulators with Canonical
|
|
190
|
+
Polynomial Invariants*. [arXiv:2609.13194v2](https://arxiv.org/abs/2609.13194v2).
|
|
191
|
+
|
|
192
|
+
[CITATION.cff](CITATION.cff), [BibTeX](references.bib), and
|
|
193
|
+
[reuse and licence notices](THIRD_PARTY.md) preserve the attribution. MIT licensed.
|
|
194
|
+
|
|
195
|
+
## What it has found so far
|
|
196
|
+
|
|
197
|
+
From [VALIDATION.md](VALIDATION.md); each row is scoped to the supplied probes.
|
|
198
|
+
|
|
199
|
+
| Case | Result |
|
|
200
|
+
|---|---|
|
|
201
|
+
| Gymnasium CartPole `masscart`/`masspole`/`length` assignment | FAIL (no-op / stale / partly stale) vs a freshly constructed reference; `gravity` PASS |
|
|
202
|
+
| MuJoCo 3.16.0 and 3.17.0, RK4 heavy top on a ball joint | FAIL: 23/24 probes below declared order 4 (1/24 ABSTAIN), median slope 1.96 |
|
|
203
|
+
| MuJoCo **4.0.1**, same model and probes | PASS 24/24, median slope 3.92 |
|
|
204
|
+
| Same model with three hinges (control) | PASS in every tested engine |
|
|
205
|
+
| Reacher post-step observation vs refreshed map | FAIL, largest discrepancy 2.4e-6 (3.16.0, 3.17.0 and 4.0.1) |
|
|
206
|
+
|
|
207
|
+
MuJoCo 4.0.0 itself was not installed or tested; no result is inferred for it.
|
|
208
|
+
|
|
209
|
+
## Roadmap
|
|
210
|
+
|
|
211
|
+
Not implemented in 0.1.0a1, and not claimed anywhere in this package:
|
|
212
|
+
|
|
213
|
+
- **0.2:** energy-refinement fingerprint (model-versus-discretisation label), a
|
|
214
|
+
decision table combining checks, and a one-parameter fit, each only with its
|
|
215
|
+
negative-control suite passing.
|
|
216
|
+
- **0.3:** algebraic invariant screening behind an optional extra.
|
|
217
|
+
- Other classic-control assignment adapters, vector environments, and shipped
|
|
218
|
+
Ant/Humanoid with contacts and actuation.
|
|
219
|
+
|
|
220
|
+
## Documentation
|
|
221
|
+
|
|
222
|
+
| File | Purpose |
|
|
223
|
+
|---|---|
|
|
224
|
+
| [docs/ADAPTERS.md](docs/ADAPTERS.md) | Writing an adapter and declaring its features |
|
|
225
|
+
| [docs/REPORTS.md](docs/REPORTS.md) | Report schema, statuses and JSON round trip |
|
|
226
|
+
| [docs/REQUIREMENTS.md](docs/REQUIREMENTS.md) | Acceptance criteria recorded before validation |
|
|
227
|
+
| [VALIDATION.md](VALIDATION.md) | What was actually run, and the limits of those runs |
|
|
228
|
+
| [validation/PROTOCOL.md](validation/PROTOCOL.md) | Paper-case protocol and scripts |
|
|
229
|
+
| [evidence/README.md](evidence/README.md) | Archived result files and the hash receipt |
|
|
230
|
+
| [CHANGELOG.md](CHANGELOG.md) | Release history |
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
# simaudit
|
|
2
|
+
|
|
3
|
+
Installable diagnostics for **your own simulator**, with explicit limits on what
|
|
4
|
+
was assessed. Supply a small Python adapter; NumPy is the only core dependency.
|
|
5
|
+
Gymnasium and MuJoCo are optional examples. Version **0.1.0a1** is an alpha,
|
|
6
|
+
not a generally calibrated simulator-certification service.
|
|
7
|
+
|
|
8
|
+
## Install
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
# Pre-release: pip skips alpha versions unless you ask for them.
|
|
12
|
+
python -m pip install --pre simaudit
|
|
13
|
+
# Optional adapters and pytest integration:
|
|
14
|
+
python -m pip install --pre 'simaudit[gymnasium,mujoco,pytest]'
|
|
15
|
+
# Or from a source checkout:
|
|
16
|
+
python -m pip install .
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Python 3.11 or newer (CI matrix 3.11-3.14; local validation on 3.14.6 and 3.12.3). Nothing downloads, runs experiments, or imports an optional
|
|
20
|
+
simulator on package import. For development: `python -m pip install '.[dev]'`.
|
|
21
|
+
|
|
22
|
+
## Test your own simulator
|
|
23
|
+
|
|
24
|
+
```python
|
|
25
|
+
import numpy as np
|
|
26
|
+
from simaudit import Adapter, Feature, Report, check_order
|
|
27
|
+
|
|
28
|
+
# Wrap your simulator so each call restores every dependency of its next step.
|
|
29
|
+
def step(state, action, dt):
|
|
30
|
+
return state + dt * (state + action)
|
|
31
|
+
|
|
32
|
+
def native_reference(state, action, dt):
|
|
33
|
+
return np.asarray([(1 + dt) * state[0] + dt * action])
|
|
34
|
+
|
|
35
|
+
adapter = Adapter(
|
|
36
|
+
name="my simulator", step_fn=step, native_step_fn=native_reference,
|
|
37
|
+
features=[Feature("dynamics", "tested", "Smooth scalar ODE, no hidden state", True)],
|
|
38
|
+
smooth=True, declared_order=1, dt=0.2,
|
|
39
|
+
)
|
|
40
|
+
result = check_order(adapter, [[0.5], [1.0], [1.5]], [0, 0, 0])
|
|
41
|
+
report = Report([result])
|
|
42
|
+
print(report.summary())
|
|
43
|
+
report.write("audit.json")
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
The native reference should call the original simulator without the adapter
|
|
47
|
+
transformation being tested. A user-provided reference and inventory are trusted
|
|
48
|
+
inputs, not automatically verified specifications. See [the adapter guide](docs/ADAPTERS.md)
|
|
49
|
+
for mutable simulators, noise, arbitrary state metrics, assignment factories,
|
|
50
|
+
observation maps and capability requirements.
|
|
51
|
+
|
|
52
|
+
The CLI loads a trusted local `module:function` returning an adapter. From this
|
|
53
|
+
directory, run the included NumPy-only example:
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
PYTHONPATH=examples simaudit order --factory custom_simulator:factory \
|
|
57
|
+
--probes examples/probes.json --strict --json audit.json
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Python factories execute code with your Python process's privileges. Probe files
|
|
61
|
+
contain only `states` and matching `actions`. The same interface works with your
|
|
62
|
+
own importable adapter module. `python -m simaudit` is equivalent to `simaudit`.
|
|
63
|
+
|
|
64
|
+
## Four independent checks
|
|
65
|
+
|
|
66
|
+
| Check | Python API | Needs | What it reports |
|
|
67
|
+
|---|---|---|---|
|
|
68
|
+
| Parameter assignment | `check_assignment` | Replay, native reference, assignment factory | Baseline/direct/fresh comparison; no-op, stale, consistent or unresolved |
|
|
69
|
+
| Numerical order | `check_order` | Replay, native reference, timestep, declared order, smoothness, distance | Stable refinement slopes, below-declaration order, or inability to diagnose |
|
|
70
|
+
| Release/variant comparison | `compare_releases` | Factory and two supplied Python interpreters | First-step differences on identical inputs; later differences recorded separately |
|
|
71
|
+
| Observation consistency | `check_observation` | Replay, native reference, observed and reference observation maps | Differences above the precision/noise floor |
|
|
72
|
+
|
|
73
|
+
The package doesn't implement the papers' energy-fingerprint classifier,
|
|
74
|
+
algebraic localisation, parameter identification or automatic equation diagnosis.
|
|
75
|
+
A numerical-order pass cannot establish that equations are correct.
|
|
76
|
+
|
|
77
|
+
## Results and limitations
|
|
78
|
+
|
|
79
|
+
Every result contains **PASS, FAIL, ABSTAIN, SKIPPED, PARTIAL or BLOWUP**, a reason,
|
|
80
|
+
scope, limitations, mandatory feature coverage, modifications, versions and raw
|
|
81
|
+
probe measurements. Limitations and untested features are printed beside results.
|
|
82
|
+
|
|
83
|
+
- Float32 or a declared noise floor blocks a healthy consistency verdict in
|
|
84
|
+
checks A, B and D. A and D can still flag discrepancies above their floors.
|
|
85
|
+
- Missing capabilities return `SKIPPED`; unsupported smoothness/features give
|
|
86
|
+
`PARTIAL`; unresolved or unstable slopes give `ABSTAIN`.
|
|
87
|
+
- Nonfinite trajectories or detected engine resets give `BLOWUP`.
|
|
88
|
+
- A failed adapter replay/native-step self-test blocks downstream diagnosis.
|
|
89
|
+
- `PASS` covers the supplied probes under the declared conditions only. A wrong
|
|
90
|
+
reference, shared implementation error or unreported hidden state can escape it.
|
|
91
|
+
|
|
92
|
+
By default `FAIL` exits 1; other nonpasses print a warning and exit 0. `--strict`
|
|
93
|
+
exits 1 on **any** nonpass. Configuration errors exit 2. Empty reports are never
|
|
94
|
+
all-clear. [Report contract](docs/REPORTS.md).
|
|
95
|
+
|
|
96
|
+
## Optional adapters and releases
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
simaudit assignment --factory simaudit.adapters.cartpole:cartpole \
|
|
100
|
+
--probes examples/cartpole-probes.json --parameter masscart --value 2 --json cartpole.json
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
This deliberately detects the paper's assignment discrepancy and exits 1. The
|
|
104
|
+
fresh CartPole reference explicitly rebuilds dependent constants; it does not
|
|
105
|
+
assert that Gymnasium promises arbitrary attribute mutation.
|
|
106
|
+
|
|
107
|
+
`mujoco_xml(xml)` supplies native replay for smooth autonomous rigid models and
|
|
108
|
+
rejects unsupported features. `reacher()` supplies a cache-consistency comparison
|
|
109
|
+
against the shipped observation map after `mj_forward`. Both live in
|
|
110
|
+
`simaudit.adapters.mujoco`; imports are lazy. Custom adapters are not subject to
|
|
111
|
+
these built-in models' limitations but must declare their own.
|
|
112
|
+
|
|
113
|
+
For a release pair, create two separate environments yourself, with NumPy and
|
|
114
|
+
the simulator versions you want, then supply their Python paths:
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
simaudit release --factory simaudit.adapters.cartpole:cartpole \
|
|
118
|
+
--probes examples/cartpole-probes.json --python-a /path/a/bin/python \
|
|
119
|
+
--python-b /path/b/bin/python --strict --json releases.json
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
The worker runs the current package source in each interpreter. The package does
|
|
123
|
+
not install or upgrade either environment. Your custom factory module must also
|
|
124
|
+
be importable there. Missing versions remain untested. A changed first step means
|
|
125
|
+
compatibility changed, not necessarily that either release is wrong.
|
|
126
|
+
|
|
127
|
+
## pytest and verification
|
|
128
|
+
|
|
129
|
+
Install the `pytest` extra to use the auto-discovered `simaudit_check` fixture:
|
|
130
|
+
|
|
131
|
+
```python
|
|
132
|
+
def test_simulator(simaudit_check):
|
|
133
|
+
simaudit_check(check_order(adapter, [[1.0]], [0]))
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
`FAIL` fails the test; other nonpasses skip with their original status and full
|
|
137
|
+
reason. `pytest --simaudit-strict --simaudit-json results.json` fails the session
|
|
138
|
+
on any nonpass and preserves the separate audit counts. Reporting is serial;
|
|
139
|
+
pytest-xdist aggregation is not supported.
|
|
140
|
+
|
|
141
|
+
Run software tests with `python -m pytest`. Build with
|
|
142
|
+
`python -m build`. See [validation results](VALIDATION.md),
|
|
143
|
+
[prospective paper-case protocol](validation/PROTOCOL.md), and
|
|
144
|
+
[requirements and acceptance criteria](docs/REQUIREMENTS.md). These are small package tests
|
|
145
|
+
and selected reproductions, not a general false-alarm guarantee.
|
|
146
|
+
|
|
147
|
+
## Cite the two method papers
|
|
148
|
+
|
|
149
|
+
1. **Tesfay Zemuy Gebrekidan** (2026). *Diagnosing Model and Discretisation Errors
|
|
150
|
+
in Reinforcement Learning Simulators Using Invariants*.
|
|
151
|
+
[Zenodo](https://zenodo.org/records/24114609).
|
|
152
|
+
2. **Tesfay Zemuy Gebrekidan and Hadush Hailu Gebrerufael** (2026).
|
|
153
|
+
*Diagnosing Faults in Reinforcement Learning Simulators with Canonical
|
|
154
|
+
Polynomial Invariants*. [arXiv:2609.13194v2](https://arxiv.org/abs/2609.13194v2).
|
|
155
|
+
|
|
156
|
+
[CITATION.cff](CITATION.cff), [BibTeX](references.bib), and
|
|
157
|
+
[reuse and licence notices](THIRD_PARTY.md) preserve the attribution. MIT licensed.
|
|
158
|
+
|
|
159
|
+
## What it has found so far
|
|
160
|
+
|
|
161
|
+
From [VALIDATION.md](VALIDATION.md); each row is scoped to the supplied probes.
|
|
162
|
+
|
|
163
|
+
| Case | Result |
|
|
164
|
+
|---|---|
|
|
165
|
+
| Gymnasium CartPole `masscart`/`masspole`/`length` assignment | FAIL (no-op / stale / partly stale) vs a freshly constructed reference; `gravity` PASS |
|
|
166
|
+
| MuJoCo 3.16.0 and 3.17.0, RK4 heavy top on a ball joint | FAIL: 23/24 probes below declared order 4 (1/24 ABSTAIN), median slope 1.96 |
|
|
167
|
+
| MuJoCo **4.0.1**, same model and probes | PASS 24/24, median slope 3.92 |
|
|
168
|
+
| Same model with three hinges (control) | PASS in every tested engine |
|
|
169
|
+
| Reacher post-step observation vs refreshed map | FAIL, largest discrepancy 2.4e-6 (3.16.0, 3.17.0 and 4.0.1) |
|
|
170
|
+
|
|
171
|
+
MuJoCo 4.0.0 itself was not installed or tested; no result is inferred for it.
|
|
172
|
+
|
|
173
|
+
## Roadmap
|
|
174
|
+
|
|
175
|
+
Not implemented in 0.1.0a1, and not claimed anywhere in this package:
|
|
176
|
+
|
|
177
|
+
- **0.2:** energy-refinement fingerprint (model-versus-discretisation label), a
|
|
178
|
+
decision table combining checks, and a one-parameter fit, each only with its
|
|
179
|
+
negative-control suite passing.
|
|
180
|
+
- **0.3:** algebraic invariant screening behind an optional extra.
|
|
181
|
+
- Other classic-control assignment adapters, vector environments, and shipped
|
|
182
|
+
Ant/Humanoid with contacts and actuation.
|
|
183
|
+
|
|
184
|
+
## Documentation
|
|
185
|
+
|
|
186
|
+
| File | Purpose |
|
|
187
|
+
|---|---|
|
|
188
|
+
| [docs/ADAPTERS.md](docs/ADAPTERS.md) | Writing an adapter and declaring its features |
|
|
189
|
+
| [docs/REPORTS.md](docs/REPORTS.md) | Report schema, statuses and JSON round trip |
|
|
190
|
+
| [docs/REQUIREMENTS.md](docs/REQUIREMENTS.md) | Acceptance criteria recorded before validation |
|
|
191
|
+
| [VALIDATION.md](VALIDATION.md) | What was actually run, and the limits of those runs |
|
|
192
|
+
| [validation/PROTOCOL.md](validation/PROTOCOL.md) | Paper-case protocol and scripts |
|
|
193
|
+
| [evidence/README.md](evidence/README.md) | Archived result files and the hash receipt |
|
|
194
|
+
| [CHANGELOG.md](CHANGELOG.md) | Release history |
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Method provenance and reuse
|
|
2
|
+
|
|
3
|
+
The package uses two source papers, with their full authorship preserved:
|
|
4
|
+
|
|
5
|
+
1. **Tesfay Zemuy Gebrekidan.** *Diagnosing Model and Discretisation Errors in
|
|
6
|
+
Reinforcement Learning Simulators Using Invariants* (2026).
|
|
7
|
+
[Zenodo record](https://zenodo.org/records/24114609).
|
|
8
|
+
Source repository: [`dafd`](https://github.com/TesfayZ/dafd). Assignment consistency, refinement diagnostics,
|
|
9
|
+
native integrator controls and precision/coverage limitations inform this package.
|
|
10
|
+
2. **Tesfay Zemuy Gebrekidan and Hadush Hailu Gebrerufael.** *Diagnosing Faults in
|
|
11
|
+
Reinforcement Learning Simulators with Canonical Polynomial Invariants* (2026).
|
|
12
|
+
[arXiv:2609.13194v2](https://arxiv.org/abs/2609.13194v2).
|
|
13
|
+
Source repository: [`algebraicRLtest`](https://github.com/TesfayZ/algebraicRLtest). Paired release comparisons, positive
|
|
14
|
+
controls and observation consistency inform this package. Its algebraic
|
|
15
|
+
screening/localisation method is not implemented in this alpha.
|
|
16
|
+
|
|
17
|
+
Both source repositories have MIT licences, copyright 2026 Tesfay Zemuy Gebrekidan.
|
|
18
|
+
That software licence notice does not replace the papers' scholarly authorship.
|
|
19
|
+
The MIT notice is retained in LICENSE. `validation/known_cases.py` adapts the
|
|
20
|
+
minimal heavy-top geometry from `dafd/benchmark_quaternion_mechanism.py`; its
|
|
21
|
+
collision masks are explicitly changed for the conservative test. The small
|
|
22
|
+
checks are new adapter-based implementations of the documented methods. No
|
|
23
|
+
research runner, paper prose, dataset, model or complete source module is vendored.
|
|
24
|
+
Source file hashes inspected during development appear in evidence/reuse_sources.json.
|
|
25
|
+
|
|
26
|
+
Runtime dependencies are imported, not bundled: NumPy (BSD-3-Clause), Gymnasium
|
|
27
|
+
(MIT, optional), MuJoCo (Apache-2.0, optional), pytest (MIT, optional). A stable
|
|
28
|
+
release still needs a full resolved dependency licence inventory, including
|
|
29
|
+
transitive dependencies. This list does not claim that audit has been completed.
|
|
30
|
+
|
|
31
|
+
Official interfaces consulted: [MuJoCo API](https://mujoco.readthedocs.io/en/stable/APIreference/APIfunctions.html)
|
|
32
|
+
and [pytest plugin documentation](https://docs.pytest.org/en/stable/how-to/writing_plugins.html).
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
# Validation of 0.1.0a1
|
|
2
|
+
|
|
3
|
+
Local measurements, 11 November 2026. Protocols were written before package
|
|
4
|
+
measurements: docs/REQUIREMENTS.md (software controls) and validation/PROTOCOL.md
|
|
5
|
+
(paper cases). Full results are in evidence/*.json. These are new package runs,
|
|
6
|
+
not imports of the papers' result tables and not an independent scientific
|
|
7
|
+
replication of their complete studies.
|
|
8
|
+
|
|
9
|
+
## Paper cases
|
|
10
|
+
|
|
11
|
+
Both installed combinations were exercised: **Gymnasium 1.4.0 / MuJoCo 3.16.0**
|
|
12
|
+
and **Gymnasium 1.3.0 / MuJoCo 3.17.0**, under Python 3.14.6.
|
|
13
|
+
|
|
14
|
+
| Case | Result in both combinations | Scope |
|
|
15
|
+
|---|---|---|
|
|
16
|
+
| CartPole masscart x2 | FAIL: no-op relative to fresh reference | 100 supplied starts, alternate actions |
|
|
17
|
+
| CartPole masspole x2 | FAIL: stale constants | Same 100 starts |
|
|
18
|
+
| CartPole length x2 | FAIL: partly stale update | Mechanism annotation comes from adapter source inspection |
|
|
19
|
+
| CartPole gravity x2 | PASS: consistent | 100 starts, no observed discrepancies |
|
|
20
|
+
| RK4 heavy top, ball joint | FAIL: 23/24 probes below declared order; 1/24 ABSTAIN | Median fine-pair slope 1.95918; conservative geometry, h=.05 |
|
|
21
|
+
| RK4 heavy top, three hinges | PASS: 24/24 probes | Median fine-pair slope 3.86154; healthy geometry control |
|
|
22
|
+
| Reacher post-step observation | FAIL: discrepancy from refreshed map | 24 starts; largest discrepancy 2.39071e-6 |
|
|
23
|
+
|
|
24
|
+
No 4.0.0 engine was installed or tested. No result is inferred for it, for other
|
|
25
|
+
release versions, or for shipped Ant/Humanoid with contacts and actuation.
|
|
26
|
+
|
|
27
|
+
### Addendum, 14 November 2026: MuJoCo 4.0.1
|
|
28
|
+
|
|
29
|
+
The same protocol and seed were run unchanged with Gymnasium 1.4.0 / MuJoCo 4.0.1
|
|
30
|
+
under Python 3.12.3 (evidence/paper_cases_mj401.json). CartPole results are
|
|
31
|
+
identical. The heavy-top **ball-joint case now PASSes on 24/24 probes** (median
|
|
32
|
+
fine-pair slope 3.915; it was FAIL with 1.959 on 3.16.0), and the hinge control
|
|
33
|
+
still passes (3.862). The Reacher observation check still FAILs (largest
|
|
34
|
+
discrepancy 2.39071e-6). This is a measurement on the conservative heavy-top
|
|
35
|
+
model only; it does not establish where the change occurred between 3.17.0 and
|
|
36
|
+
4.0.1, and 4.0.0 remains untested.
|
|
37
|
+
|
|
38
|
+
### Addendum: release-worker isolation bug (fixed before first publication)
|
|
39
|
+
|
|
40
|
+
When simaudit was pip-installed, `compare_releases` put the caller's whole
|
|
41
|
+
site-packages directory on the workers' PYTHONPATH, which could make both workers
|
|
42
|
+
import the caller's NumPy/simulator instead of each interpreter's own versions.
|
|
43
|
+
The validation runs above used a source tree, where only the package directory
|
|
44
|
+
was exposed, so they were not affected. Workers now receive a temporary directory
|
|
45
|
+
containing only the simaudit package, with a regression test. The Acrobot release
|
|
46
|
+
controls were not rerun after this change.
|
|
47
|
+
The Reacher reference shares the observation formula and only checks its cache
|
|
48
|
+
consistency. CartPole's fresh reference rebuilds constants under an explicitly
|
|
49
|
+
supplied parameter contract; direct attribute mutation need not be an upstream
|
|
50
|
+
supported API. A FAIL here is scoped to that comparison.
|
|
51
|
+
|
|
52
|
+
The two runs reuse the same probe design and are not 48 independent starts for
|
|
53
|
+
an engine-wide population claim. The ball-joint abstention is retained. No
|
|
54
|
+
threshold was relaxed to remove it.
|
|
55
|
+
|
|
56
|
+
## Release comparison and controls from the joint paper
|
|
57
|
+
|
|
58
|
+
Using isolated workers, Gymnasium 1.3.0 versus 1.4.0 Acrobot agreed exactly on
|
|
59
|
+
all recorded first steps. On the same 1.3.0 version, the book-versus-nips control
|
|
60
|
+
was detected (maximum first-step difference 0.00147277) and the mass2=1.3 control
|
|
61
|
+
was detected (0.0609411). Each used 24 paired starts, 5 recorded steps, and the
|
|
62
|
+
first-step threshold 1e-13. This validates detection, not the joint paper's
|
|
63
|
+
algebraic screening, constraint localisation or parameter attribution, which
|
|
64
|
+
are deferred features. The full historical 350-pair matrix was not rerun.
|
|
65
|
+
|
|
66
|
+
## General-purpose software controls
|
|
67
|
+
|
|
68
|
+
Euler and RK4 scalar ODE controls each had 0 failures and 0 abstentions across
|
|
69
|
+
100 starts. The two-sided 95% Wilson false-alarm interval per control is
|
|
70
|
+
approximately [0, 0.0370]. This is conditional on those chosen scalar controls,
|
|
71
|
+
not calibration across arbitrary simulators. Float32 order and observation
|
|
72
|
+
controls abstain; declared-noise order controls abstain. Float64 observation
|
|
73
|
+
controls pass. Tests also cover missing capabilities, bad restoration, wrong
|
|
74
|
+
native references, nonfinite trajectories, unknown features, unlogged assignment
|
|
75
|
+
modifications, schema rejection, CLI, subprocess comparison and pytest statuses.
|
|
76
|
+
|
|
77
|
+
Implementation testing exposed two issues which were corrected before final
|
|
78
|
+
measurements: independent float32 arithmetic needed a dtype-aware replay
|
|
79
|
+
comparison floor, and returned mutable buffers needed copying. The frozen
|
|
80
|
+
scientific diagnostic thresholds were not changed. A zero-torque constant-speed
|
|
81
|
+
hinge test was corrected to use a gravity-loaded axis; exact flow correctly
|
|
82
|
+
abstains because there is no truncation error to estimate.
|
|
83
|
+
|
|
84
|
+
## Packaging and software test result
|
|
85
|
+
|
|
86
|
+
The final suite has 34 passing tests (33 at the 11 November receipt, plus the isolation regression test; 34 pass on Python 3.12.3 with MuJoCo 4.0.1), including the optional MuJoCo tests. A wheel
|
|
87
|
+
and source distribution were built. The wheel was installed without network
|
|
88
|
+
access into a new virtual environment using existing system dependencies, and its
|
|
89
|
+
CLI ran a copied custom-simulator example from /tmp, outside the source tree.
|
|
90
|
+
The installed package imported without loading Gymnasium or MuJoCo, and pytest
|
|
91
|
+
discovered its plugin entry point. This is package isolation, not a clean-room
|
|
92
|
+
reinstallation of all third-party dependencies.
|
|
93
|
+
|
|
94
|
+
Run `python validation/verify_receipt.py` from the repository to check retained
|
|
95
|
+
hashes. The repository receipt is excluded from distributions to avoid a
|
|
96
|
+
self-referential archive hash.
|
|
97
|
+
|
|
98
|
+
## Reproduce
|
|
99
|
+
|
|
100
|
+
From this package directory, after installing the package:
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
python -m pytest
|
|
104
|
+
python -m build
|
|
105
|
+
PYTHONPATH=validation python validation/run_controls.py \
|
|
106
|
+
--report /tmp/package_controls.json --summary /tmp/false_alarm_controls.json
|
|
107
|
+
PYTHONPATH=src:validation /path/to/simulator/python validation/run_known_cases.py /tmp/paper_cases.json
|
|
108
|
+
PYTHONPATH=src:validation python validation/run_release_cases.py \
|
|
109
|
+
--python-a /path/to/version-a/python --python-b /path/to/version-b/python \
|
|
110
|
+
--out /tmp/release_controls.json
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
`evidence/validation_receipt.json` records final execution checks and hashes.
|
|
114
|
+
A GitHub Actions workflow is included (.github/workflows/tests.yml); hosted CI had
|
|
115
|
+
not been run when this receipt was written. Stable publication still requires
|
|
116
|
+
external usability tests, a larger held-out control set, a complete dependency
|
|
117
|
+
licence inventory and additional version coverage. The name `simaudit` was
|
|
118
|
+
unregistered on PyPI on 14 November 2026.
|