plesty-sdk 0.3.6__tar.gz → 0.5.0.dev1__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 (115) hide show
  1. {plesty_sdk-0.3.6 → plesty_sdk-0.5.0.dev1}/CHANGELOG.md +214 -0
  2. {plesty_sdk-0.3.6 → plesty_sdk-0.5.0.dev1}/PKG-INFO +7 -3
  3. {plesty_sdk-0.3.6 → plesty_sdk-0.5.0.dev1}/README.md +6 -2
  4. {plesty_sdk-0.3.6 → plesty_sdk-0.5.0.dev1}/REUSE.toml +8 -0
  5. {plesty_sdk-0.3.6 → plesty_sdk-0.5.0.dev1}/docs/architecture.md +72 -27
  6. {plesty_sdk-0.3.6 → plesty_sdk-0.5.0.dev1}/docs/commands/check.md +6 -4
  7. {plesty_sdk-0.3.6 → plesty_sdk-0.5.0.dev1}/docs/commands/docs.md +5 -5
  8. {plesty_sdk-0.3.6 → plesty_sdk-0.5.0.dev1}/docs/commands/init.md +136 -42
  9. {plesty_sdk-0.3.6 → plesty_sdk-0.5.0.dev1}/docs/commands/update.md +11 -1
  10. {plesty_sdk-0.3.6 → plesty_sdk-0.5.0.dev1}/docs/getting-started.md +25 -7
  11. {plesty_sdk-0.3.6 → plesty_sdk-0.5.0.dev1}/docs/index.md +2 -2
  12. {plesty_sdk-0.3.6 → plesty_sdk-0.5.0.dev1}/gl-secret-detection-report.json +6 -6
  13. plesty_sdk-0.5.0.dev1/packaging/versioning.py +72 -0
  14. {plesty_sdk-0.3.6 → plesty_sdk-0.5.0.dev1}/plesty/sdk/assets/init/field-test.py.template +12 -1
  15. {plesty_sdk-0.3.6 → plesty_sdk-0.5.0.dev1}/plesty/sdk/assets/init/next-steps.yaml +3 -1
  16. {plesty_sdk-0.3.6 → plesty_sdk-0.5.0.dev1}/plesty/sdk/assets/templates/extras/analyzer/analyzer.py +5 -0
  17. {plesty_sdk-0.3.6 → plesty_sdk-0.5.0.dev1}/plesty/sdk/assets/templates/extras/device/base_device.py +3 -3
  18. {plesty_sdk-0.3.6 → plesty_sdk-0.5.0.dev1}/plesty/sdk/assets/templates/extras/device/device.py +6 -0
  19. plesty_sdk-0.5.0.dev1/plesty/sdk/assets/templates/extras/device/preflight.py +389 -0
  20. {plesty_sdk-0.3.6 → plesty_sdk-0.5.0.dev1}/plesty/sdk/assets/templates/extras/experiment/experiment.py +10 -8
  21. {plesty_sdk-0.3.6 → plesty_sdk-0.5.0.dev1}/plesty/sdk/assets/templates/template/README.md.jinja +12 -1
  22. {plesty_sdk-0.3.6 → plesty_sdk-0.5.0.dev1}/plesty/sdk/assets/templates/template/docs/index.md.jinja +16 -0
  23. plesty_sdk-0.5.0.dev1/plesty/sdk/assets/templates/template/packaging/versioning.py +72 -0
  24. plesty_sdk-0.5.0.dev1/plesty/sdk/assets/templates/template/plesty/{{module_name}}/{% if project_type in ['device', 'experiment'] %}__main__.py{% endif %}.jinja +275 -0
  25. {plesty_sdk-0.3.6 → plesty_sdk-0.5.0.dev1}/plesty/sdk/assets/templates/template/pyproject.toml.jinja +17 -1
  26. {plesty_sdk-0.3.6 → plesty_sdk-0.5.0.dev1}/plesty/sdk/assets/templates/template/tests/test_{{module_name}}.py.jinja +23 -10
  27. plesty_sdk-0.5.0.dev1/plesty/sdk/assets/templates/template/tests/{% if project_type == 'device' %}test_preflight.py{% endif %}.jinja +293 -0
  28. plesty_sdk-0.5.0.dev1/plesty/sdk/assets/templates/template/{% if project_type == 'experiment' %}config{% endif %}/default.yaml +11 -0
  29. plesty_sdk-0.5.0.dev1/plesty/sdk/assets/templates/template/{% if project_type == 'experiment' %}setup.yaml{% endif %} +24 -0
  30. plesty_sdk-0.5.0.dev1/plesty/sdk/assets/templates/template/{% if project_type in ['device', 'experiment'] %}.env.example{% endif %}.jinja +55 -0
  31. {plesty_sdk-0.3.6 → plesty_sdk-0.5.0.dev1}/plesty/sdk/commands/check.py +307 -3
  32. {plesty_sdk-0.3.6 → plesty_sdk-0.5.0.dev1}/plesty/sdk/commands/field_test.py +69 -0
  33. {plesty_sdk-0.3.6 → plesty_sdk-0.5.0.dev1}/plesty/sdk/commands/init.py +51 -3
  34. {plesty_sdk-0.3.6 → plesty_sdk-0.5.0.dev1}/plesty/sdk/commands/mock_test.py +33 -3
  35. plesty_sdk-0.5.0.dev1/plesty/sdk/commands/update.py +277 -0
  36. {plesty_sdk-0.3.6 → plesty_sdk-0.5.0.dev1}/plesty/sdk/report.py +1 -0
  37. {plesty_sdk-0.3.6 → plesty_sdk-0.5.0.dev1}/plesty/sdk/semver.py +60 -18
  38. {plesty_sdk-0.3.6 → plesty_sdk-0.5.0.dev1}/pyproject.toml +14 -1
  39. plesty_sdk-0.5.0.dev1/tests/test_check_experiment_manifest.py +360 -0
  40. plesty_sdk-0.5.0.dev1/tests/test_device_preflight_template.py +241 -0
  41. {plesty_sdk-0.3.6 → plesty_sdk-0.5.0.dev1}/tests/test_field_test_scaffold.py +31 -0
  42. {plesty_sdk-0.3.6 → plesty_sdk-0.5.0.dev1}/tests/test_init.py +210 -12
  43. {plesty_sdk-0.3.6 → plesty_sdk-0.5.0.dev1}/tests/test_mock_test_scaffold.py +78 -0
  44. {plesty_sdk-0.3.6 → plesty_sdk-0.5.0.dev1}/tests/test_semver.py +80 -2
  45. {plesty_sdk-0.3.6 → plesty_sdk-0.5.0.dev1}/tests/test_update.py +110 -0
  46. plesty_sdk-0.5.0.dev1/tests/test_versioning_rule.py +125 -0
  47. {plesty_sdk-0.3.6 → plesty_sdk-0.5.0.dev1}/uv.lock +48 -12
  48. plesty_sdk-0.3.6/plesty/sdk/assets/templates/template/plesty/{{module_name}}/{% if project_type in ['device', 'experiment'] %}__main__.py{% endif %}.jinja +0 -216
  49. plesty_sdk-0.3.6/plesty/sdk/assets/templates/template/{% if project_type == 'experiment' %}config{% endif %}/default.yaml +0 -6
  50. plesty_sdk-0.3.6/plesty/sdk/assets/templates/template/{% if project_type in ['device', 'experiment'] %}.env.example{% endif %}.jinja +0 -38
  51. plesty_sdk-0.3.6/plesty/sdk/commands/update.py +0 -141
  52. {plesty_sdk-0.3.6 → plesty_sdk-0.5.0.dev1}/.coverage +0 -0
  53. {plesty_sdk-0.3.6 → plesty_sdk-0.5.0.dev1}/.gitignore +0 -0
  54. {plesty_sdk-0.3.6 → plesty_sdk-0.5.0.dev1}/.gitlab-ci.yml +0 -0
  55. {plesty_sdk-0.3.6 → plesty_sdk-0.5.0.dev1}/.mailmap +0 -0
  56. {plesty_sdk-0.3.6 → plesty_sdk-0.5.0.dev1}/LICENSE +0 -0
  57. {plesty_sdk-0.3.6 → plesty_sdk-0.5.0.dev1}/LICENSES/LGPL-3.0-or-later.txt +0 -0
  58. {plesty_sdk-0.3.6 → plesty_sdk-0.5.0.dev1}/docs/commands/license.md +0 -0
  59. {plesty_sdk-0.3.6 → plesty_sdk-0.5.0.dev1}/docs/commands/manual.md +0 -0
  60. {plesty_sdk-0.3.6 → plesty_sdk-0.5.0.dev1}/docs/toc.yaml +0 -0
  61. {plesty_sdk-0.3.6 → plesty_sdk-0.5.0.dev1}/plesty/sdk/__init__.py +0 -0
  62. {plesty_sdk-0.3.6 → plesty_sdk-0.5.0.dev1}/plesty/sdk/assets/check/guidance.yaml +0 -0
  63. {plesty_sdk-0.3.6 → plesty_sdk-0.5.0.dev1}/plesty/sdk/assets/docs-build-gitlab-ci.yml +0 -0
  64. {plesty_sdk-0.3.6 → plesty_sdk-0.5.0.dev1}/plesty/sdk/assets/hub-theme/plesty-hub.css +0 -0
  65. {plesty_sdk-0.3.6 → plesty_sdk-0.5.0.dev1}/plesty/sdk/assets/hub-theme/plesty-logo.svg +0 -0
  66. {plesty_sdk-0.3.6 → plesty_sdk-0.5.0.dev1}/plesty/sdk/assets/hub-theme/plesty-mark.svg +0 -0
  67. {plesty_sdk-0.3.6 → plesty_sdk-0.5.0.dev1}/plesty/sdk/assets/hub-theme/plesty-versions.css +0 -0
  68. {plesty_sdk-0.3.6 → plesty_sdk-0.5.0.dev1}/plesty/sdk/assets/hub-theme/plesty-versions.js +0 -0
  69. {plesty_sdk-0.3.6 → plesty_sdk-0.5.0.dev1}/plesty/sdk/assets/init/client-field-test.py.template +0 -0
  70. {plesty_sdk-0.3.6 → plesty_sdk-0.5.0.dev1}/plesty/sdk/assets/init/field-test-env.example +0 -0
  71. {plesty_sdk-0.3.6 → plesty_sdk-0.5.0.dev1}/plesty/sdk/assets/init/field-test-questions.yaml +0 -0
  72. {plesty_sdk-0.3.6 → plesty_sdk-0.5.0.dev1}/plesty/sdk/assets/init/mock-test.py.template +0 -0
  73. {plesty_sdk-0.3.6 → plesty_sdk-0.5.0.dev1}/plesty/sdk/assets/manual/pdf-common.css +0 -0
  74. {plesty_sdk-0.3.6 → plesty_sdk-0.5.0.dev1}/plesty/sdk/assets/manual/pdf-page-landscape.css +0 -0
  75. {plesty_sdk-0.3.6 → plesty_sdk-0.5.0.dev1}/plesty/sdk/assets/manual/pdf-page-portrait.css +0 -0
  76. {plesty_sdk-0.3.6 → plesty_sdk-0.5.0.dev1}/plesty/sdk/assets/ruff.toml +0 -0
  77. {plesty_sdk-0.3.6 → plesty_sdk-0.5.0.dev1}/plesty/sdk/assets/templates/copier.yml +0 -0
  78. {plesty_sdk-0.3.6 → plesty_sdk-0.5.0.dev1}/plesty/sdk/assets/templates/template/.gitignore +0 -0
  79. {plesty_sdk-0.3.6 → plesty_sdk-0.5.0.dev1}/plesty/sdk/assets/templates/template/CHANGELOG.md.jinja +0 -0
  80. {plesty_sdk-0.3.6 → plesty_sdk-0.5.0.dev1}/plesty/sdk/assets/templates/template/LICENSE +0 -0
  81. {plesty_sdk-0.3.6 → plesty_sdk-0.5.0.dev1}/plesty/sdk/assets/templates/template/LICENSES/LGPL-3.0-or-later.txt +0 -0
  82. {plesty_sdk-0.3.6 → plesty_sdk-0.5.0.dev1}/plesty/sdk/assets/templates/template/REUSE.toml.jinja +0 -0
  83. {plesty_sdk-0.3.6 → plesty_sdk-0.5.0.dev1}/plesty/sdk/assets/templates/template/docs/toc.yaml +0 -0
  84. {plesty_sdk-0.3.6 → plesty_sdk-0.5.0.dev1}/plesty/sdk/assets/templates/template/plesty/{{module_name}}/__init__.py.jinja +0 -0
  85. {plesty_sdk-0.3.6 → plesty_sdk-0.5.0.dev1}/plesty/sdk/assets/templates/template/plesty/{{module_name}}/config.yaml.jinja +0 -0
  86. {plesty_sdk-0.3.6 → plesty_sdk-0.5.0.dev1}/plesty/sdk/assets/templates/template/tests/__init__.py.jinja +0 -0
  87. {plesty_sdk-0.3.6 → plesty_sdk-0.5.0.dev1}/plesty/sdk/authorship.py +0 -0
  88. {plesty_sdk-0.3.6 → plesty_sdk-0.5.0.dev1}/plesty/sdk/cache.py +0 -0
  89. {plesty_sdk-0.3.6 → plesty_sdk-0.5.0.dev1}/plesty/sdk/cli.py +0 -0
  90. {plesty_sdk-0.3.6 → plesty_sdk-0.5.0.dev1}/plesty/sdk/commands/__init__.py +0 -0
  91. {plesty_sdk-0.3.6 → plesty_sdk-0.5.0.dev1}/plesty/sdk/commands/docs/__init__.py +0 -0
  92. {plesty_sdk-0.3.6 → plesty_sdk-0.5.0.dev1}/plesty/sdk/commands/docs/deploy.py +0 -0
  93. {plesty_sdk-0.3.6 → plesty_sdk-0.5.0.dev1}/plesty/sdk/commands/docs/generate.py +0 -0
  94. {plesty_sdk-0.3.6 → plesty_sdk-0.5.0.dev1}/plesty/sdk/commands/docs/picker.py +0 -0
  95. {plesty_sdk-0.3.6 → plesty_sdk-0.5.0.dev1}/plesty/sdk/commands/docs/serve.py +0 -0
  96. {plesty_sdk-0.3.6 → plesty_sdk-0.5.0.dev1}/plesty/sdk/commands/docs/sphinx_builder.py +0 -0
  97. {plesty_sdk-0.3.6 → plesty_sdk-0.5.0.dev1}/plesty/sdk/commands/license.py +0 -0
  98. {plesty_sdk-0.3.6 → plesty_sdk-0.5.0.dev1}/plesty/sdk/commands/manual.py +0 -0
  99. {plesty_sdk-0.3.6 → plesty_sdk-0.5.0.dev1}/plesty/sdk/context.py +0 -0
  100. {plesty_sdk-0.3.6 → plesty_sdk-0.5.0.dev1}/plesty/sdk/tui.py +0 -0
  101. {plesty_sdk-0.3.6 → plesty_sdk-0.5.0.dev1}/tests/__init__.py +0 -0
  102. {plesty_sdk-0.3.6 → plesty_sdk-0.5.0.dev1}/tests/test_authorship.py +0 -0
  103. {plesty_sdk-0.3.6 → plesty_sdk-0.5.0.dev1}/tests/test_cache.py +0 -0
  104. {plesty_sdk-0.3.6 → plesty_sdk-0.5.0.dev1}/tests/test_check.py +0 -0
  105. {plesty_sdk-0.3.6 → plesty_sdk-0.5.0.dev1}/tests/test_cli.py +0 -0
  106. {plesty_sdk-0.3.6 → plesty_sdk-0.5.0.dev1}/tests/test_context.py +0 -0
  107. {plesty_sdk-0.3.6 → plesty_sdk-0.5.0.dev1}/tests/test_docs_deploy.py +0 -0
  108. {plesty_sdk-0.3.6 → plesty_sdk-0.5.0.dev1}/tests/test_docs_generate.py +0 -0
  109. {plesty_sdk-0.3.6 → plesty_sdk-0.5.0.dev1}/tests/test_docs_picker.py +0 -0
  110. {plesty_sdk-0.3.6 → plesty_sdk-0.5.0.dev1}/tests/test_docs_serve.py +0 -0
  111. {plesty_sdk-0.3.6 → plesty_sdk-0.5.0.dev1}/tests/test_license.py +0 -0
  112. {plesty_sdk-0.3.6 → plesty_sdk-0.5.0.dev1}/tests/test_manual.py +0 -0
  113. {plesty_sdk-0.3.6 → plesty_sdk-0.5.0.dev1}/tests/test_report.py +0 -0
  114. {plesty_sdk-0.3.6 → plesty_sdk-0.5.0.dev1}/tests/test_sphinx_builder.py +0 -0
  115. {plesty_sdk-0.3.6 → plesty_sdk-0.5.0.dev1}/tests/test_tui.py +0 -0
@@ -2,6 +2,220 @@
2
2
 
3
3
  The format is based on [Keep a Changelog](https://keepachangelog.com).
4
4
 
5
+ ## [0.5.0.dev1] — 2026-09-25
6
+
7
+ Development pre-release, the first of the 0.5 line: scaffolds generated against
8
+ plesty-lib 0.5 (`PLESTY_DATA`, `setup.yaml`, `ExperimentCLI`, device preflight) need an SDK that
9
+ writes them before 0.5.0 is ready to be called finished.
10
+
11
+ ### Added
12
+ - **A snapshot is named for the release its line is working towards.**
13
+ versioningit's default `next_version` bumps the minor, which is wrong on
14
+ both lines governance#20 runs: six commits after `v0.4.0`, `maintain/0.4`
15
+ built `0.5.0.dev6`, which sorts *above* the `0.4.1` the line exists to
16
+ produce, so a bench given that snapshot is never upgraded by the release;
17
+ and `exp` sitting on `v0.5.0.dev3` built `0.6.0.devN`, a minor nobody is
18
+ working towards.
19
+
20
+ `plesty init` now ships `packaging/versioning.py` and points
21
+ `[tool.hatch.version.next-version]` at it: `exp` and `main` take the next
22
+ minor, every other branch the release its last tag precedes — the next
23
+ patch on `maintain/0.N`. A branch that names no line, including the detached
24
+ HEAD CI checks out, takes the patch, which understates and so can never
25
+ shadow a release. A build at a tag is unaffected.
26
+
27
+ An existing module takes the same rule with `plesty update --versioning`,
28
+ which writes the file, adds the block, and annotates `packaging/**` in
29
+ `REUSE.toml` unless an annotation already covers it — versioningit imports
30
+ the rule at build time, and the `.pyc` Python writes beside it fails gate 9
31
+ otherwise. plesty-sdk itself now builds with the rule (plesty-sdk#41).
32
+ - **Every scaffolded device module ships a preflight.** A module installs,
33
+ configures and serves happily with no instrument behind it: the transport
34
+ logs that it found nothing, the server binds regardless, and a client can
35
+ hold a resource against a device that was never there. `plesty init device`
36
+ now generates `plesty/<module>/preflight.py`, and both entry points call it
37
+ before touching the instrument — `python -m plesty.<module>` before the
38
+ server binds, `tests/field_test.py` before a run. It refuses only an
39
+ instrument the transport looked for and did not find; a transport that
40
+ cannot tell without opening answers *unknown*, and unknown serves.
41
+
42
+ It answers the four questions that were being answered by hand in
43
+ plesty-k10cr1 and plesty-lightfield-spectrometer: whether the operating
44
+ system enumerated the instrument and bound a driver, what discovery resolved
45
+ and what it saw, which `.env` is in effect and whether its server port agrees
46
+ with the one the fleet handed over as `--tcp-port`, and which version of the
47
+ module is installed and whether it came from PyPI or a checkout. The
48
+ transport half is `TrafficManager.preflight()` (plesty-lib#50, >= 0.5.0.dev7);
49
+ the module reaches it through one author hook, `transport()`, filled in
50
+ beside `Device.connect()`. `--skip-preflight` serves anyway. The generated
51
+ `tests/test_preflight.py` mocks the transport, so the logic runs in CI with
52
+ nothing attached and the module stays over the coverage gate. `summary()` is
53
+ the only output that may name a serial number; what a report file gets is
54
+ `to_dict()`, which carries none (plesty-sdk#39).
55
+
56
+ `plesty init field-test` reads whether the module has a `preflight.py`: a
57
+ module scaffolded before this release gets a `preflight()` that says what is
58
+ missing and lets the run go on, so the generator keeps working on every
59
+ module the hub already has.
60
+
61
+ The `plesty-lib` floor rises to `0.5.0.dev7`, the first whose transports
62
+ answer `preflight()`.
63
+ - **Gate e3 — Experiment Manifest.** An experiment module now has a run
64
+ contract to declare (`plesty.lib.experiment.ExperimentCLI`, plesty-lib#54),
65
+ and nothing enforced it: a module could drift back to a hand-rolled
66
+ `argparse` and every console offering its settings would silently go empty.
67
+
68
+ For `module_type = "experiment"`, `python -m <module> --manifest` must print
69
+ the declaration as JSON; the gate checks that it names this module, that
70
+ every configuration parameter carries a `dtype` and a `description` — a
71
+ control rendered without them is a box with a name on it — and that a
72
+ bounded parameter carries a `unit`, or its bounds read as bare numbers.
73
+
74
+ It then compares the schema with the module's own `config/*.yaml`. A key the
75
+ configuration sets and the schema omits cannot be edited by anything reading
76
+ the manifest; a key the schema declares and no configuration sets renders as
77
+ a setting that does nothing. That drift is the reason the gate exists: a
78
+ schema nobody keeps current is worse than none, because a console renders it
79
+ as fact.
80
+
81
+ - **`plesty init` scaffolds an experiment onto `ExperimentCLI`.** The generated
82
+ `__main__.py` declares a `Manifest` and a `SCHEMA` matching the scaffolded
83
+ `config/default.yaml`, and registers a rig builder and an experiment builder
84
+ in place of its own `argparse` — so a new module passes e3 as scaffolded, and
85
+ gets `--config`, `--resume`, `--run-root`, `--dry-run` and `--manifest`
86
+ without writing them. The experiment entry point no longer calls
87
+ `run_server` itself: `ExperimentCLI.main` does, so the loop pyzmq needs is
88
+ the library's guarantee rather than something each scaffold must remember.
89
+
90
+ The `plesty-lib` floor rises to `0.5.0.dev4`, the first with `ExperimentCLI`.
91
+ - **Gate 8 baselines against the line being built.** It compared the working
92
+ tree against the newest release of the whole project, which is right only
93
+ while there is one line. A `maintain/x.y` branch takes fixes to its own
94
+ features and merges upward only, so it never gains what a later line added:
95
+ once that line publishes, every function it introduced would read as a
96
+ removal from the older tree, on every run, for as long as the line is
97
+ supported. The baseline is now the newest release at or below the tree's own
98
+ `major.minor`, taken from the line the checkout can reach by tag. Pre-release
99
+ and yanked files are skipped. Nothing changes on the newest line.
100
+
101
+ ### Fixed
102
+ - **A scaffolded experiment ships the `setup.yaml` plesty-lib requires**
103
+ (plesty-sdk#43). plesty-lib 0.5.0.dev6 made every experiment module declare
104
+ its benches and devices in a committed `setup.yaml`; `plesty init experiment`
105
+ wrote none. It now writes one — `bench-local` with `data: ${LOCAL_DATA}`, a
106
+ commented remote-bench example, and a `device` entry for the scaffold's
107
+ `RIG` naming its bench — and `.env.example` suggests the variables it names:
108
+ `LOCAL_DATA`, empty, so the first real run stops and says to set it, with
109
+ `DEVICE_ADDRESS` and `PLESTY_DATA` kept as commented overrides for running
110
+ without a bench. The manifest declares `LOCAL_DATA`. A module scaffolded
111
+ earlier adds the two files by hand; `hub/experiments/plesty-pol-pl` has a
112
+ working pair.
113
+ - **A scaffolded experiment names the shared disk the way its library reads
114
+ it.** plesty-lib 0.5.0.dev6 replaced `PLESTY_DATA_DIR` and
115
+ `PLESTY_DATA_MOUNT` with one variable, `PLESTY_DATA` — the shared disk as
116
+ each machine sees it, with every path between machines relative to it — and
117
+ the scaffold, already pinned above that release, kept writing the two old
118
+ names into `.env.example` and the manifest. A module scaffolded from it set
119
+ variables nothing read, so its runs silently went to `./runs` instead of the
120
+ share. `.env.example`, the manifest and the next steps now carry
121
+ `PLESTY_DATA`, and the generated `experiment.py` routes a writing device with
122
+ `set_data_path(self.raw_dir, subdirs=True)` and records its files with
123
+ `raw_file()`. A module scaffolded earlier migrates by hand: set `PLESTY_DATA`
124
+ where `PLESTY_DATA_MOUNT` was and drop `PLESTY_DATA_DIR`.
125
+ - **A scaffolded experiment passes its own tests.** When the entry point moved
126
+ onto `ExperimentCLI`, the generated `tests/test_<module>.py` was not moved
127
+ with it: it still imported `load_config` and called `build_devices(address)`,
128
+ neither of which the new `__main__.py` has, so every experiment module failed
129
+ three of its tests — and gate 7 — as scaffolded. The tests now go through the
130
+ module's `cli`: `cli.load()` for `--config`, `build_devices(args, config,
131
+ skip)`, and a `--dry-run` of the shipped `config/default.yaml`. Every project
132
+ type's generated tests are now run in plesty-sdk's own suite, against the
133
+ plesty-lib its dev group now requires, the scaffold's own floor
134
+ (`>=0.5.0.dev7`, was `>=0.3.6`) — tested against an older library, generated
135
+ code can only fail to import, which is how this went unnoticed.
136
+ - **`plesty init mock-test` runs on a scaffolded device.** Every device module
137
+ exports `DocDevice = partial(Device, id=...)`, and when no simulator address
138
+ applies — a fresh scaffold has none — the generated test handed that partial
139
+ to `DevicePipeline`, which constructs from a class and names it by
140
+ `__name__`: the test crashed before any gate ran, so gate d1 could not pass.
141
+ The file now passes `Device` and forwards the partial's own arguments
142
+ (`DevicePipeline(Device, *DocDevice.args, **DocDevice.keywords)`), the same
143
+ construction read at run time. A `DocDevice` that is neither a class nor a
144
+ partial of `Device` is refused with that reason. The probe also rejects a
145
+ `check_errors()` that returns anything but a list, as gate 7 does, and the
146
+ device template's stub returns `[]` under the `list[str]` contract
147
+ plesty-lib declares instead of `None`.
148
+ - The scaffolded analyzer template carries its REUSE header. Every other
149
+ template declares copyright and licence inline; this one declared nothing, so
150
+ the file linted as uncovered and its author was credited nowhere — and every
151
+ project scaffolded from it inherited that gap.
152
+
153
+ ## [0.4.0] — 2026-09-07
154
+
155
+ A scaffolded device can now be driven by hand. `plesty init device` generates a
156
+ `--gui` entry point that opens the module's control panel — every control built
157
+ from the device schema, nothing hand-written — as an alternative to serving the
158
+ instrument, so a new module is usable at the bench before anyone writes a client
159
+ for it. Qt rides in a `gui` extra, so a headless bench that only ever serves the
160
+ device pays nothing for the window it never opens. Generated projects pin
161
+ `plesty-lib>=0.4.0`, the release `plesty.lib.ui` begins at.
162
+
163
+ ### Added
164
+ - **A scaffolded device offers its control panel.** `plesty init device` now
165
+ generates `--gui` in the module's `__main__.py`: it opens plesty-lib's
166
+ `DevicePanel` over a `LocalHandle` on the instrument in this process, as an
167
+ alternative to serving it — both want the one session the instrument grants,
168
+ so `main()` does one or the other. Nothing about the window is hand-written;
169
+ the controls, their bounds, units and tooltips come from the device schema, so
170
+ a parameter added there appears in the panel.
171
+
172
+ The generated `open_panel()` calls `connect()` **before** the panel is built.
173
+ That ordering is the point of the entry point rather than an implementation
174
+ detail: what the instrument reports on connection narrows the schema's
175
+ device-wide envelope to the unit in front of the operator, and those are the
176
+ bounds worth typing into. On a PM100D the fitted head answers 400–1100 nm
177
+ against the 185–25000 nm the schema declares for every head.
178
+
179
+ Qt lands in a new `gui` extra (`plesty-lib[gui]`), not in `dependencies` — a
180
+ headless bench serves the device and never opens a window — and the
181
+ `plesty.lib.ui` import is local to `open_panel`, so serving pulls no toolkit.
182
+ Every module used to hand-add this entry point; `plesty-pm100d` was the last
183
+ to do it by hand.
184
+
185
+ - The device template's note on `kind` now says what declaring `lifecycle` does
186
+ beyond the field test: an operation whose effect outlives the call is kept off
187
+ a control panel — the local one and the one a remote client renders. The
188
+ schema is the only place either can learn it.
189
+
190
+ ### Changed
191
+ - `PLESTY_LIB_VERSION`, the `plesty-lib` floor a scaffold falls back to when
192
+ PyPI is unreachable, moves `0.3.6` → `0.4.0`. That is the release
193
+ `plesty.lib.ui` begins at, so it is the floor the generated `--gui` needs;
194
+ below it the panel path raises `ImportError`. The live PyPI lookup already
195
+ resolved 0.4.0 on any machine with a route out — this only changes the
196
+ offline case.
197
+
198
+ ### Fixed
199
+ - **The pre-push hook no longer leaks git's hook variables into the gates.**
200
+ Git exports `GIT_DIR` and friends into every hook, and everything the hook
201
+ runs inherits them, so git work nested inside the gates resolves the pushing
202
+ repository instead of the one it is standing in. The hook now clears them
203
+ before running `plesty check`; nothing about what the gates check changes.
204
+
205
+ Two failures this had already caused. Gate 10 has pip clone each git-pinned
206
+ dependency into a temp directory, where versioningit read the outer
207
+ repository's tag and commit — on lightfield_spectrometer (2026-09-04) a
208
+ pinned `plesty-lib` came out as `0.1.0.dev65+<the module's sha>`, which
209
+ cannot satisfy the module's own floor, so the audit failed with a resolution
210
+ error unrelated to the code. That is enough to make anyone drop the pin
211
+ rather than debug it, and a dropped pin is a module quietly building against
212
+ the wrong dependency. Separately, a test suite that drives git could commit
213
+ into the real repository rather than its fixture, which is what made pushing
214
+ this repo from a worktree unsafe.
215
+
216
+ Existing modules keep the old hook until `plesty init` is re-run; the fix is
217
+ a one-line `unset` in `.git/hooks/pre-push` for anyone who does not want to.
218
+
5
219
  ## [0.3.6] — 2026-09-03
6
220
 
7
221
  Everything the scaffolds generate now passes the gates the scaffolds ask for.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: plesty-sdk
3
- Version: 0.3.6
3
+ Version: 0.5.0.dev1
4
4
  Summary: Command-line application for developing Python modules compliant with the Plesty standard.
5
5
  Author: Plesty Development Team, Yunshuang Yuan, Christopher Borchers, fvrehlinger, Maximilian Heller, Michael Zopf
6
6
  License-Expression: LGPL-3.0-or-later
@@ -55,10 +55,14 @@ plesty init device --author "Jane Doe" --email jane@example.com
55
55
 
56
56
  # 2. Develop your module — the pre-push hook runs compliance checks automatically
57
57
 
58
- # 3. Run the full [SDK] compliance check manually at any time
58
+ # 3. Drive the instrument from a control panel built out of your device schema
59
+ uv sync --extra gui
60
+ python -m plesty.power_meter --gui
61
+
62
+ # 4. Run the full [SDK] compliance check manually at any time
59
63
  plesty check
60
64
 
61
- # 4. Preview the documentation locally
65
+ # 5. Preview the documentation locally
62
66
  plesty docs serve
63
67
  ```
64
68
 
@@ -23,10 +23,14 @@ plesty init device --author "Jane Doe" --email jane@example.com
23
23
 
24
24
  # 2. Develop your module — the pre-push hook runs compliance checks automatically
25
25
 
26
- # 3. Run the full [SDK] compliance check manually at any time
26
+ # 3. Drive the instrument from a control panel built out of your device schema
27
+ uv sync --extra gui
28
+ python -m plesty.power_meter --gui
29
+
30
+ # 4. Run the full [SDK] compliance check manually at any time
27
31
  plesty check
28
32
 
29
- # 4. Preview the documentation locally
33
+ # 5. Preview the documentation locally
30
34
  plesty docs serve
31
35
  ```
32
36
 
@@ -37,3 +37,11 @@ path = [
37
37
  SPDX-FileCopyrightText = "2025-2026 Plesty Development Team"
38
38
  SPDX-FileContributor = "Yunshuang Yuan"
39
39
  SPDX-License-Identifier = "LGPL-3.0-or-later"
40
+
41
+ # versioningit imports `packaging/versioning.py` at build time, so Python
42
+ # writes a `.pyc` beside it that `reuse lint` sees even though `.gitignore`
43
+ # hides it from git.
44
+ [[annotations]]
45
+ path = ["packaging/**"]
46
+ SPDX-FileCopyrightText = "2026 Plesty Development Team"
47
+ SPDX-License-Identifier = "LGPL-3.0-or-later"
@@ -17,9 +17,9 @@ into five areas:
17
17
  ```{mermaid}
18
18
  graph TD
19
19
  CLI["CLI layer<br/>cli.py · context.py"]
20
- CHECK["Check engine<br/>commands/check.py · authorship.py"]
20
+ CHECK["Check engine<br/>commands/check.py · authorship.py · semver.py · report.py"]
21
21
  DOCS["Docs builder<br/>commands/docs/*"]
22
- SCAFFOLD["Scaffolding<br/>commands/init.py · commands/update.py"]
22
+ SCAFFOLD["Scaffolding<br/>commands/init.py · update.py · field_test.py · mock_test.py"]
23
23
  LICENSE["Licensing<br/>commands/license.py"]
24
24
  MANUAL["Manuals<br/>commands/manual.py"]
25
25
  ASSETS["Packaged assets<br/>assets/*"]
@@ -39,9 +39,9 @@ graph TD
39
39
  | Area | Modules | Responsibility |
40
40
  |---|---|---|
41
41
  | CLI layer | `cli.py`, `context.py` | Click command group; resolves `--project-dir` into an `ApplicationContext` that lazily parses `pyproject.toml` and derives `DocumentationSettings`. Every command receives this context — no command parses `pyproject.toml` itself. |
42
- | Check engine | `commands/check.py`, `authorship.py` | The compliance gates behind `plesty check`. Each gate is a plain function raising `click.ClickException` (or exiting with the failing tool's code) on violation. `authorship.py` holds the git-history authorship analysis shared with `plesty license author`. |
43
- | Docs builder | `commands/docs/` (`serve.py`, `deploy.py`, `sphinx_builder.py`, `generate.py`) | Sphinx configuration and build (`sphinx_builder.py`), live-reload preview (`serve`), pages-branch deployment with versioned releases (`deploy`), and zero-effort device reference-page generation from a live `doc_model()` (`generate.py`). `cache.py` maintains the git-ignored `.plesty/` build cache used by `serve`. |
44
- | Scaffolding | `commands/init.py`, `commands/update.py` | Project generation from the copier template (`plesty init`) and in-place maintenance of existing projects (`plesty update`). `update` reuses `init`'s CI-generation and hook-installation helpers so both commands emit identical artifacts. |
42
+ | Check engine | `commands/check.py`, `authorship.py`, `semver.py`, `report.py` | The compliance gates behind `plesty check`. Each gate is a plain function raising `click.ClickException` (or exiting with the failing tool's code) on violation. `authorship.py` holds the git-history authorship analysis shared with `plesty license author`; `semver.py` the public-API diff against the published release for gate 8; `report.py` the `reports/check.json` document every run writes. |
43
+ | Docs builder | `commands/docs/` (`serve.py`, `deploy.py`, `sphinx_builder.py`, `generate.py`, `picker.py`) | Sphinx configuration and build (`sphinx_builder.py`), live-reload preview (`serve`), pages-branch deployment with versioned releases (`deploy`), zero-effort device reference-page generation from a live `doc_model()` (`generate.py`), and stamping the version picker into release trees built before it existed (`picker.py`). `cache.py` maintains the git-ignored `.plesty/` build cache used by `serve`. |
44
+ | Scaffolding | `commands/init.py`, `commands/update.py`, `commands/field_test.py`, `commands/mock_test.py`, `tui.py` | Project generation from the copier template (`plesty init`) and in-place maintenance of existing projects (`plesty update`). `update` reuses `init`'s CI-generation and hook-installation helpers so both commands emit identical artifacts. `field_test.py` and `mock_test.py` generate a device module's field test and mock pipeline test (`plesty init field-test` / `mock-test`); `tui.py` is the dependency-free checkable list the field-test interview uses. |
45
45
  | Licensing and manuals | `commands/license.py`, `commands/manual.py` | SPDX header maintenance via the REUSE tool, and Markdown/PDF manual generation for external (non-hub) device packages. |
46
46
  | Packaged assets | `plesty/sdk/assets/` | Data shipped inside the wheel: the canonical `ruff.toml`, the copier scaffold under `templates/`, the shared Sphinx theme (`hub-theme/`), and the pages-branch CI file (`docs-build-gitlab-ci.yml`). Assets are always accessed through `importlib.resources`, never by path relative to the source tree. |
47
47
 
@@ -92,11 +92,11 @@ engine is re-run by CI (`hub-check` in the `plesty-standard-ci` component) as th
92
92
  authoritative final gate. Gates carry a tier label:
93
93
 
94
94
  - **`[SDK+CI]`** (gates 1–10) — enforced locally and repeated in CI.
95
- - **`[SDK]`** (gate 11, docs build) — local-only. When `CI=true` (set on every GitLab
96
- runner) gate 11 reports itself skipped, because the dedicated `hub-deploy-docs` CI job
97
- builds the docs instead. Tests exercising the Sphinx path must therefore clear the `CI`
98
- variable.
99
- - **`[Device]` / `[Analyzer]` / `[Experiment]`** (d1–d3, a1, e1–e2) — module-type gates,
95
+ - **`[SDK]`** (gate 11, docs build) — runs locally and in merge-request pipelines. In a
96
+ branch pipeline (`CI=true` without `CI_PIPELINE_SOURCE=merge_request_event`) gate 11
97
+ reports itself skipped, because the dedicated `hub-deploy-docs` CI job builds the docs
98
+ instead. Tests exercising the Sphinx path must therefore clear the `CI` variable.
99
+ - **`[Device]` / `[Analyzer]` / `[Experiment]`** (d1–d3, a1, e1–e3) — module-type gates,
100
100
  active only when `[tool.plesty] module_type` is `"device"`, `"analyzer"`, or
101
101
  `"experiment"` respectively; every other module type gets an explicit "N/A" line, so
102
102
  the gate list is identical everywhere and activation is declarative, never heuristic.
@@ -104,7 +104,9 @@ authoritative final gate. Gates carry a tier label:
104
104
  return-annotation contract, d3 that the device renders a populated `doc_model()`;
105
105
  a1 verifies the five `AnalyzerPipeline` contract tests; e1 verifies the five
106
106
  `ExperimentPipeline` contract tests, e2 that persistence goes through the unified
107
- data layer.
107
+ data layer, e3 that `python -m <module> --manifest` publishes the run contract and
108
+ that its configuration schema and the module's own `config/*.yaml` say the same
109
+ thing.
108
110
 
109
111
  The standard selects how deep the run goes: `pixel` runs gates 1–2, `nebula` gates 1–6
110
112
  (static, no network), `quantum` everything including the module-type gates. The full
@@ -161,14 +163,24 @@ tradeoff is losing pydata-specific features (notably its version switcher UI); v
161
163
  now surfaced through Sphinx's plain `version`/`release` fields and the deploy-side
162
164
  `versions.json`.
163
165
 
164
- **Schema-usage enforcement inside the data-layer gate.** Gate 4 statically rejects classes
165
- that shadow a plesty-lib schema model (`DeviceStatus`, `ResultDocument`, `Plan`, …), and
166
- gates d2/e2 enforce the telemetry annotations and `save_result` persistence path. The
167
- alternative — trusting convention and review — had already produced silent forks of the
168
- data contract. AST-based enforcement is cheap, runs offline in the pre-push hook, and needs
169
- no imports of the target package; the tradeoff is that static analysis can only catch what
170
- it can resolve (e.g. a dynamically computed `open()` mode is deliberately left alone), so
171
- the gates are a floor, not a proof.
166
+ **Schema-usage enforcement inside the data-layer gate.** plesty-lib defines the data every
167
+ module exchanges — a device's configuration and operations, its status and telemetry, an
168
+ experiment's plan and journal, a stored result — as a fixed set of classes (the *schema
169
+ models*). A module that wrote its own class of the same name — its own `DeviceStatus`, say —
170
+ would *shadow* the library's: code in that module would silently use the local copy, which
171
+ no other module, console or reader understands. Gate 4 therefore scans every Python file
172
+ under `plesty/<module>/` of **every** module type — device, analyzer, experiment and core
173
+ alike — and fails on a class definition whose name is one of plesty-lib's schema models
174
+ (`ConfigParameter`, `FuncMeta`, `DeviceStatus`, `TelemetryEvent`, `ResultDocument`, `Plan`,
175
+ `Journal`, … — the full list is `_PROTECTED_SCHEMA_NAMES` in `commands/check.py`), and on a
176
+ public function without a return type annotation. Two further checks apply to one module
177
+ type each: gate d2 (devices) requires `status()` to be annotated `DeviceStatus`, and gate e2
178
+ (experiments) requires results to be stored through `save_result` rather than written by
179
+ hand. The alternative — trusting convention and review — had already produced silent forks
180
+ of the data contract. Reading the source as a syntax tree, without importing it, is cheap,
181
+ runs offline in the pre-push hook, and cannot be fooled by a module that fails to import;
182
+ the tradeoff is that such a check sees only what is written literally (a file mode computed
183
+ at run time is deliberately left alone), so the gates are a floor, not a proof.
172
184
 
173
185
  **Generated device documentation (gate d3) over hand-written reference pages.**
174
186
  Hand-written parameter and operation tables drift the moment a schema changes. Rendering
@@ -187,6 +199,44 @@ have already diverged. The cost is a coupling to the component's ref and input c
187
199
  a rename or ref change in plesty-ci breaks scaffolds (see technical debt below) — which we
188
200
  accept because centralised evolution has proven far cheaper than fleet-wide file surgery.
189
201
 
202
+ **A generated device carries its own control panel, with Qt in an extra.** The panel is
203
+ plesty-lib's `DevicePanel`, and it reaches a device only through a four-call interface,
204
+ `DeviceHandle` — `describe()`, `query()`, `write()`, `call()`. Where the device is decides
205
+ only which handle the panel is given: in the same process, the device object wrapped in a
206
+ `LocalHandle`; over the network, a TCP client (`DeviceTCPIPClient`) satisfies the same
207
+ interface; from a remote `plesty-bench`, the bench agent relays the same four calls. So
208
+ the one panel drives a device locally or remotely alike. What the scaffold generates is the
209
+ local case: `--gui` on the module's existing entry point opens the panel on the machine the
210
+ instrument is plugged into, rather than shipping a separate console or leaving each module
211
+ to hand-write one.
212
+
213
+ `--gui` and serving are alternatives *within one process*: both open the instrument, and
214
+ most instruments grant a single session, so `python -m plesty.<module>` does one or the
215
+ other. Both at once is the remote case — serve the device, and open the panel from a client
216
+ on the same or another machine.
217
+
218
+ The controls are built from `describe()`, the device's schema serialized as plain data, so
219
+ a module gets a usable window the moment its schema is right, and a hole in that payload
220
+ surfaces at the bench instead of only across a network. Operations are filtered by their
221
+ declared `kind`, which makes the schema the single place that decides what a panel offers,
222
+ local or remote.
223
+
224
+ A module with a GUI of its own is not overridden by anything: `--gui` is ordinary generated
225
+ code in the module's `__main__.py` (`open_panel()`), and the module replaces it — with its
226
+ own `Panel` subclass, several panels in one window, or no panel — by editing that function.
227
+ Nothing loads a module's panel code from elsewhere.
228
+
229
+ Qt lives in a generated `gui` extra rather than in the generated `dependencies` — a headless
230
+ bench serves the device and never opens a window — which is why `plesty.lib.ui` is imported
231
+ inside the panel path and not at module scope. How far Qt reaches is deliberately bounded:
232
+ the panel contract (`Panel`), the device handle, the worker thread that keeps device calls
233
+ off the window's thread, and the `describe()` payload import no toolkit; Qt is confined to
234
+ `plesty.lib.ui.qt` and to the widget each panel builds. Moving to another toolkit, or
235
+ offering a second kind of interface such as a web page, would therefore mean new widget
236
+ code and a new shell, while the data a panel renders and the way it reaches a device stay
237
+ as they are. The tradeoff is that this widget code is Qt today, and a second toolkit would
238
+ be a second implementation of it rather than a switch.
239
+
190
240
  **`plesty update --install-hook` for cloned projects.** Git hooks live in `.git/` and are
191
241
  never cloned, so `plesty init`'s pre-push hook silently vanishes for every contributor
192
242
  after the first. Rather than adopting a hook-manager dependency (pre-commit) or committing
@@ -216,6 +266,8 @@ The sanctioned runtime dependencies (from `[project] dependencies` in `pyproject
216
266
  | `sphinxcontrib-mermaid` | Mermaid diagrams in docs (including this page). |
217
267
  | `livereload` / `watchfiles` | Local preview server and rebuild-on-change for `plesty docs serve`. |
218
268
  | `md2pdf` | PDF rendering for `plesty manual generate --with-pdf`. |
269
+ | `griffe` | Static public-API extraction for the gate 8 diff — the analyzed package is never imported. |
270
+ | `packaging` | Version and specifier parsing (gate 8 baselines, `--lib-version`, versioned docs deploy). |
219
271
  | `pyside6-essentials` (extra: `gui`) | Optional GUI support; not installed by default. |
220
272
 
221
273
  **Policy:** a new third-party library may only be added if it is actively maintained
@@ -231,11 +283,4 @@ as a GitLab issue labeled
231
283
  [`type::debt`](https://gitlab.com/plesty/core/plesty-sdk/-/issues/?label_name%5B%5D=type%3A%3Adebt)
232
284
  — per the
233
285
  [governance DoD](https://gitlab.com/plesty/governance/-/blob/main/CONTRIBUTING.md),
234
- permanent architectural debt is filed before the shortcut lands. Two standing caveats
235
- matter when reading the gate architecture above: gate 11 (docs build) never runs in
236
- merge-request pipelines — the zero-warning build is enforced by the pre-push hook and,
237
- post-merge, by `hub-deploy-docs`
238
- ([sdk#22](https://gitlab.com/plesty/core/plesty-sdk/-/issues/22)) — and the authoritative
239
- CI-side semantic-versioning API diff that gate 8 defers to does not exist yet, so version
240
- enforcement is advisory
241
- ([plesty-ci#10](https://gitlab.com/plesty/core/plesty-ci/-/issues/10)).
286
+ permanent architectural debt is filed before the shortcut lands.
@@ -39,8 +39,9 @@ Gates are divided into two tiers depending on where they are enforced:
39
39
 
40
40
  - **`[SDK+CI]`** — enforced locally by `plesty check` and repeated by CI in a clean
41
41
  environment as the authoritative final gate (gates 1–10).
42
- - **`[SDK]`** — enforced locally only; skipped in CI (`CI=true`). Currently just the
43
- Docs Build gate (11), which CI covers with its dedicated `hub-deploy-docs` job.
42
+ - **`[SDK]`** — enforced locally and in merge-request pipelines; skipped in branch
43
+ pipelines, where the dedicated `hub-deploy-docs` job builds the docs instead. Currently
44
+ just the Docs Build gate (11).
44
45
 
45
46
  The pre-push git hook installed by `plesty init` runs `plesty check` before every push,
46
47
  enforcing whichever standard resolves for the project (see Standard resolution above).
@@ -75,10 +76,10 @@ release. Required for every PyPI release, and the derived default from version `
75
76
  | Gate | Tier | What is checked |
76
77
  |---|---|---|
77
78
  | Automated Test Coverage | `[SDK+CI]` | `pytest --cov` with a minimum threshold of **80%** |
78
- | Semantic Versioning | `[SDK+CI]` | Reminder to tag a new release when commits exist since the last tag; API-level diff against the published PyPI release runs in CI |
79
+ | Semantic Versioning | `[SDK+CI]` | Diffs the public API against the newest PyPI release on the tree's own `major.minor` line (so a `maintain/0.N` branch is compared with `0.N`, not with a later line) and requires a version bump that matches: a minor bump for a breaking change before `1.0.0`, a major bump from then on. Advisory locally, fails in CI. When the package is unpublished or PyPI is unreachable, falls back to a reminder to tag a release when commits exist since the last tag |
79
80
  | Licensing Compliance | `[SDK+CI]` | [REUSE](https://reuse.software/) compliance (`reuse lint`) and authorship currency of the SPDX headers; `license` field declared in `pyproject.toml` |
80
81
  | Vulnerability Audit | `[SDK+CI]` | No known CVEs in declared dependencies; queries the OSV database via `pip-audit` |
81
- | Docs Build | `[SDK]` | Full Sphinx build must complete with zero errors and zero warnings. Local-only — skipped in CI (`CI=true`), where the dedicated `hub-deploy-docs` job builds the docs |
82
+ | Docs Build | `[SDK]` | Full Sphinx build must complete with zero errors and zero warnings. Runs locally and in merge-request pipelines, so a docs-breaking change fails before it merges; skipped in branch pipelines, where the dedicated `hub-deploy-docs` job builds the docs |
82
83
 
83
84
  ### Module-type gates
84
85
 
@@ -93,6 +94,7 @@ they report N/A for every other module type.
93
94
  | a1 — Analyzer Contract | `module_type = "analyzer"` | The five `AnalyzerPipeline` contract gate tests (`plesty.lib.test.analyzer_pipeline`) are present in `tests/` and pass on schema-generated synthetic inputs: public `Analyzer` subclass, schema integrity, analyze-signature match, mock round-trip, provenance stamp |
94
95
  | e1 — Experiment Contract | `module_type = "experiment"` | The five `ExperimentPipeline` contract gate tests (`plesty.lib.test.experiment_pipeline`) are present in `tests/` and pass: public `Experiment` subclass, deterministic `build_plan()`, resolvable step ops, JSON-serializable plan, async lifecycle hooks |
95
96
  | e2 — Experiment Persistence | `module_type = "experiment"` | No ad-hoc persistence in the module package (`np.save`/`np.savez`, `pickle.dump`, `h5py.File`/`open` with write mode) — results are persisted via `plesty.lib.data.save_result`/`ResultDocument` |
97
+ | e3 — Experiment Manifest | `module_type = "experiment"` | `python -m <module> --manifest` prints the run contract (`plesty.lib.experiment.Manifest`): it names this module, every configuration parameter carries a `dtype` and a `description` (and a `unit` where it is bounded), and the schema and the module's own `config/*.yaml` declare the same keys — a key only one of them knows about is either uneditable or a setting that does nothing |
96
98
  | f1 — Field Test Findings | any module with a published field test report | No `reports/<instrument>/field-test.json` holds an `error` finding — a parameter the instrument refuses or will not answer, which the module nonetheless declares |
97
99
 
98
100
  ## Report artifacts
@@ -46,7 +46,7 @@ whenever a `.md` or `.rst` file changes. Useful during active documentation writ
46
46
 
47
47
  | Option | Default | Description |
48
48
  |---|---|---|
49
- | `--dev-addr` | `127.0.0.1:8000` | Host and port for the preview server |
49
+ | `--dev-addr` | `localhost:8000` | Host and port for the preview server |
50
50
 
51
51
  ### Example
52
52
 
@@ -84,19 +84,19 @@ yet exist.
84
84
  ```bash
85
85
  # Deploy development snapshot (latest)
86
86
  plesty docs deploy \
87
- https://gitlab-ci-token:${CI_BOT_TOKEN}@gitlab.com/plesty/my-device.git \
87
+ https://oauth2:${CI_BOT_TOKEN}@gitlab.com/plesty/my-device.git \
88
88
  docs-build \
89
89
  --latest
90
90
 
91
91
  # Deploy a versioned release
92
92
  plesty docs deploy \
93
- https://gitlab-ci-token:${CI_BOT_TOKEN}@gitlab.com/plesty/my-device.git \
93
+ https://oauth2:${CI_BOT_TOKEN}@gitlab.com/plesty/my-device.git \
94
94
  docs-build \
95
95
  --release
96
96
  ```
97
97
 
98
- In practice this command is called by the `deploy-docs` CI component and does not need to
99
- be run manually.
98
+ In practice this command is called by the `hub-deploy-docs` job of the `plesty-standard-ci`
99
+ component and does not need to be run manually.
100
100
 
101
101
  ---
102
102