litmus-test 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (572) hide show
  1. litmus_test-0.1.0/.gitignore +29 -0
  2. litmus_test-0.1.0/CHANGELOG.md +39 -0
  3. litmus_test-0.1.0/CONTRIBUTING.md +619 -0
  4. litmus_test-0.1.0/LICENSE +191 -0
  5. litmus_test-0.1.0/PKG-INFO +307 -0
  6. litmus_test-0.1.0/README.md +234 -0
  7. litmus_test-0.1.0/SECURITY.md +27 -0
  8. litmus_test-0.1.0/docs/architecture-erd.md +382 -0
  9. litmus_test-0.1.0/docs/audits/0.1.0-alignment.md +88 -0
  10. litmus_test-0.1.0/docs/audits/config.md +61 -0
  11. litmus_test-0.1.0/docs/audits/execution.md +102 -0
  12. litmus_test-0.1.0/docs/audits/public-api.md +127 -0
  13. litmus_test-0.1.0/docs/audits/store.md +39 -0
  14. litmus_test-0.1.0/docs/capability-examples.md +377 -0
  15. litmus_test-0.1.0/docs/capability-schema.md +260 -0
  16. litmus_test-0.1.0/docs/comparison.md +147 -0
  17. litmus_test-0.1.0/docs/concepts/architecture.md +396 -0
  18. litmus_test-0.1.0/docs/concepts/capabilities.md +359 -0
  19. litmus_test-0.1.0/docs/concepts/capability-model.md +319 -0
  20. litmus_test-0.1.0/docs/concepts/event-log.md +174 -0
  21. litmus_test-0.1.0/docs/concepts/fixtures.md +338 -0
  22. litmus_test-0.1.0/docs/concepts/flight-streaming.md +76 -0
  23. litmus_test-0.1.0/docs/concepts/outcomes.md +185 -0
  24. litmus_test-0.1.0/docs/concepts/overview.md +70 -0
  25. litmus_test-0.1.0/docs/concepts/platform-architecture.md +247 -0
  26. litmus_test-0.1.0/docs/concepts/products.md +271 -0
  27. litmus_test-0.1.0/docs/concepts/results-storage.md +101 -0
  28. litmus_test-0.1.0/docs/concepts/sessions.md +69 -0
  29. litmus_test-0.1.0/docs/concepts/stations.md +228 -0
  30. litmus_test-0.1.0/docs/concepts/step-hierarchy.md +153 -0
  31. litmus_test-0.1.0/docs/concepts/step-manifest.md +119 -0
  32. litmus_test-0.1.0/docs/concepts/three-stores.md +83 -0
  33. litmus_test-0.1.0/docs/concepts/why-event-sourcing.md +70 -0
  34. litmus_test-0.1.0/docs/concepts/why-pytest.md +51 -0
  35. litmus_test-0.1.0/docs/concepts.md +388 -0
  36. litmus_test-0.1.0/docs/connect.md +88 -0
  37. litmus_test-0.1.0/docs/explorations/api-stability-and-versioning.md +348 -0
  38. litmus_test-0.1.0/docs/explorations/data-architecture.md +290 -0
  39. litmus_test-0.1.0/docs/explorations/data-schemas.md +515 -0
  40. litmus_test-0.1.0/docs/explorations/ideal-data-architecture.md +359 -0
  41. litmus_test-0.1.0/docs/explorations/parquet-merge-plan-audit.md +224 -0
  42. litmus_test-0.1.0/docs/explorations/per-execution-step-records.md +241 -0
  43. litmus_test-0.1.0/docs/explorations/sequences-as-files.md +493 -0
  44. litmus_test-0.1.0/docs/guides/adding-instruments.md +466 -0
  45. litmus_test-0.1.0/docs/guides/configuring-stations.md +401 -0
  46. litmus_test-0.1.0/docs/guides/context-architecture.md +124 -0
  47. litmus_test-0.1.0/docs/guides/grafana-dashboards.md +137 -0
  48. litmus_test-0.1.0/docs/guides/limits.md +183 -0
  49. litmus_test-0.1.0/docs/guides/managing-sessions.md +89 -0
  50. litmus_test-0.1.0/docs/guides/mcp-integration.md +501 -0
  51. litmus_test-0.1.0/docs/guides/mock-mode.md +296 -0
  52. litmus_test-0.1.0/docs/guides/multi-dut-testing.md +156 -0
  53. litmus_test-0.1.0/docs/guides/profiles.md +306 -0
  54. litmus_test-0.1.0/docs/guides/querying-channels.md +95 -0
  55. litmus_test-0.1.0/docs/guides/querying-events.md +83 -0
  56. litmus_test-0.1.0/docs/guides/spec-driven-testing.md +130 -0
  57. litmus_test-0.1.0/docs/guides/traceability.md +349 -0
  58. litmus_test-0.1.0/docs/guides/vector-expansion.md +378 -0
  59. litmus_test-0.1.0/docs/guides/writing-sequences.md +401 -0
  60. litmus_test-0.1.0/docs/guides/writing-tests.md +389 -0
  61. litmus_test-0.1.0/docs/highlight-reel.md +794 -0
  62. litmus_test-0.1.0/docs/index.md +109 -0
  63. litmus_test-0.1.0/docs/instruments/custom-drivers.md +458 -0
  64. litmus_test-0.1.0/docs/integration/harness.md +365 -0
  65. litmus_test-0.1.0/docs/integration/instruments.md +279 -0
  66. litmus_test-0.1.0/docs/integration/lakehouse-import.md +170 -0
  67. litmus_test-0.1.0/docs/integration/logging.md +349 -0
  68. litmus_test-0.1.0/docs/integration/openhtf-adapter.md +405 -0
  69. litmus_test-0.1.0/docs/integration/overview.md +160 -0
  70. litmus_test-0.1.0/docs/integration/pytest-existing.md +337 -0
  71. litmus_test-0.1.0/docs/integration/results-api.md +306 -0
  72. litmus_test-0.1.0/docs/outputs.md +73 -0
  73. litmus_test-0.1.0/docs/overview.md +247 -0
  74. litmus_test-0.1.0/docs/quickstart.md +256 -0
  75. litmus_test-0.1.0/docs/reference/api.md +394 -0
  76. litmus_test-0.1.0/docs/reference/cli.md +647 -0
  77. litmus_test-0.1.0/docs/reference/client.md +262 -0
  78. litmus_test-0.1.0/docs/reference/configuration.md +450 -0
  79. litmus_test-0.1.0/docs/reference/connect-api.md +119 -0
  80. litmus_test-0.1.0/docs/reference/event-types.md +329 -0
  81. litmus_test-0.1.0/docs/reference/models.md +877 -0
  82. litmus_test-0.1.0/docs/reference/parquet-schema.md +636 -0
  83. litmus_test-0.1.0/docs/reference/pytest-native.md +161 -0
  84. litmus_test-0.1.0/docs/reference/pytest-plugin.md +436 -0
  85. litmus_test-0.1.0/docs/tutorial/01-first-test.md +156 -0
  86. litmus_test-0.1.0/docs/tutorial/02-mock-instruments.md +167 -0
  87. litmus_test-0.1.0/docs/tutorial/03-fixtures.md +193 -0
  88. litmus_test-0.1.0/docs/tutorial/04-limits.md +346 -0
  89. litmus_test-0.1.0/docs/tutorial/05-configuration.md +189 -0
  90. litmus_test-0.1.0/docs/tutorial/06-specifications.md +302 -0
  91. litmus_test-0.1.0/docs/tutorial/07-real-instruments.md +214 -0
  92. litmus_test-0.1.0/docs/tutorial/08-capabilities.md +237 -0
  93. litmus_test-0.1.0/docs/tutorial/09-production.md +345 -0
  94. litmus_test-0.1.0/docs/tutorial/10-live-monitoring.md +108 -0
  95. litmus_test-0.1.0/docs/tutorial/from-mocks-to-hardware.md +141 -0
  96. litmus_test-0.1.0/docs/tutorial/index.md +54 -0
  97. litmus_test-0.1.0/examples/01-vanilla/README.md +99 -0
  98. litmus_test-0.1.0/examples/01-vanilla/conftest.py +35 -0
  99. litmus_test-0.1.0/examples/01-vanilla/drivers/__init__.py +29 -0
  100. litmus_test-0.1.0/examples/01-vanilla/drivers/dmm.py +128 -0
  101. litmus_test-0.1.0/examples/01-vanilla/drivers/psu.py +122 -0
  102. litmus_test-0.1.0/examples/01-vanilla/litmus.yaml +3 -0
  103. litmus_test-0.1.0/examples/01-vanilla/pyproject.toml +16 -0
  104. litmus_test-0.1.0/examples/01-vanilla/pytest.ini +12 -0
  105. litmus_test-0.1.0/examples/01-vanilla/tests/test_rail.py +33 -0
  106. litmus_test-0.1.0/examples/02-verify/README.md +100 -0
  107. litmus_test-0.1.0/examples/02-verify/conftest.py +34 -0
  108. litmus_test-0.1.0/examples/02-verify/drivers/__init__.py +29 -0
  109. litmus_test-0.1.0/examples/02-verify/drivers/dmm.py +128 -0
  110. litmus_test-0.1.0/examples/02-verify/drivers/psu.py +122 -0
  111. litmus_test-0.1.0/examples/02-verify/litmus.yaml +3 -0
  112. litmus_test-0.1.0/examples/02-verify/pyproject.toml +16 -0
  113. litmus_test-0.1.0/examples/02-verify/pytest.ini +7 -0
  114. litmus_test-0.1.0/examples/02-verify/tests/test_rail.py +62 -0
  115. litmus_test-0.1.0/examples/03-inline-limits/README.md +37 -0
  116. litmus_test-0.1.0/examples/03-inline-limits/conftest.py +24 -0
  117. litmus_test-0.1.0/examples/03-inline-limits/drivers/__init__.py +29 -0
  118. litmus_test-0.1.0/examples/03-inline-limits/drivers/dmm.py +128 -0
  119. litmus_test-0.1.0/examples/03-inline-limits/drivers/psu.py +122 -0
  120. litmus_test-0.1.0/examples/03-inline-limits/litmus.yaml +3 -0
  121. litmus_test-0.1.0/examples/03-inline-limits/pyproject.toml +16 -0
  122. litmus_test-0.1.0/examples/03-inline-limits/pytest.ini +2 -0
  123. litmus_test-0.1.0/examples/03-inline-limits/tests/test_rail.py +42 -0
  124. litmus_test-0.1.0/examples/03-inline-limits/tests/test_sequence.py +78 -0
  125. litmus_test-0.1.0/examples/04-sidecar-markers/README.md +73 -0
  126. litmus_test-0.1.0/examples/04-sidecar-markers/conftest.py +24 -0
  127. litmus_test-0.1.0/examples/04-sidecar-markers/drivers/__init__.py +29 -0
  128. litmus_test-0.1.0/examples/04-sidecar-markers/drivers/dmm.py +128 -0
  129. litmus_test-0.1.0/examples/04-sidecar-markers/drivers/psu.py +122 -0
  130. litmus_test-0.1.0/examples/04-sidecar-markers/litmus.yaml +3 -0
  131. litmus_test-0.1.0/examples/04-sidecar-markers/pyproject.toml +16 -0
  132. litmus_test-0.1.0/examples/04-sidecar-markers/pytest.ini +2 -0
  133. litmus_test-0.1.0/examples/04-sidecar-markers/tests/test_rail.py +53 -0
  134. litmus_test-0.1.0/examples/04-sidecar-markers/tests/test_rail.yaml +25 -0
  135. litmus_test-0.1.0/examples/05-product-spec/README.md +52 -0
  136. litmus_test-0.1.0/examples/05-product-spec/conftest.py +24 -0
  137. litmus_test-0.1.0/examples/05-product-spec/drivers/__init__.py +29 -0
  138. litmus_test-0.1.0/examples/05-product-spec/drivers/dmm.py +128 -0
  139. litmus_test-0.1.0/examples/05-product-spec/drivers/psu.py +122 -0
  140. litmus_test-0.1.0/examples/05-product-spec/litmus.yaml +3 -0
  141. litmus_test-0.1.0/examples/05-product-spec/products/buck_3v3.yaml +45 -0
  142. litmus_test-0.1.0/examples/05-product-spec/pyproject.toml +16 -0
  143. litmus_test-0.1.0/examples/05-product-spec/tests/test_rail.py +52 -0
  144. litmus_test-0.1.0/examples/05-product-spec/tests/test_rail.yaml +26 -0
  145. litmus_test-0.1.0/examples/06-station-catalog/README.md +135 -0
  146. litmus_test-0.1.0/examples/06-station-catalog/catalog/generic_dmm.yaml +60 -0
  147. litmus_test-0.1.0/examples/06-station-catalog/catalog/generic_psu.yaml +76 -0
  148. litmus_test-0.1.0/examples/06-station-catalog/drivers/__init__.py +29 -0
  149. litmus_test-0.1.0/examples/06-station-catalog/drivers/dmm.py +128 -0
  150. litmus_test-0.1.0/examples/06-station-catalog/drivers/psu.py +122 -0
  151. litmus_test-0.1.0/examples/06-station-catalog/fixtures/buck_3v3_bench.yaml +19 -0
  152. litmus_test-0.1.0/examples/06-station-catalog/litmus.yaml +5 -0
  153. litmus_test-0.1.0/examples/06-station-catalog/products/buck_3v3.yaml +45 -0
  154. litmus_test-0.1.0/examples/06-station-catalog/pyproject.toml +16 -0
  155. litmus_test-0.1.0/examples/06-station-catalog/stations/bench_01.yaml +23 -0
  156. litmus_test-0.1.0/examples/06-station-catalog/tests/test_rail.py +114 -0
  157. litmus_test-0.1.0/examples/06-station-catalog/tests/test_rail.yaml +41 -0
  158. litmus_test-0.1.0/examples/07-profiles/README.md +77 -0
  159. litmus_test-0.1.0/examples/07-profiles/catalog/generic_dmm.yaml +60 -0
  160. litmus_test-0.1.0/examples/07-profiles/catalog/generic_psu.yaml +76 -0
  161. litmus_test-0.1.0/examples/07-profiles/drivers/__init__.py +29 -0
  162. litmus_test-0.1.0/examples/07-profiles/drivers/dmm.py +128 -0
  163. litmus_test-0.1.0/examples/07-profiles/drivers/psu.py +122 -0
  164. litmus_test-0.1.0/examples/07-profiles/fixtures/buck_3v3_bench.yaml +20 -0
  165. litmus_test-0.1.0/examples/07-profiles/litmus.yaml +11 -0
  166. litmus_test-0.1.0/examples/07-profiles/products/buck_3v3.yaml +45 -0
  167. litmus_test-0.1.0/examples/07-profiles/profiles/characterization.yaml +8 -0
  168. litmus_test-0.1.0/examples/07-profiles/profiles/development.yaml +12 -0
  169. litmus_test-0.1.0/examples/07-profiles/profiles/production.yaml +13 -0
  170. litmus_test-0.1.0/examples/07-profiles/profiles/rail_family.yaml +9 -0
  171. litmus_test-0.1.0/examples/07-profiles/pyproject.toml +16 -0
  172. litmus_test-0.1.0/examples/07-profiles/stations/bench_01.yaml +23 -0
  173. litmus_test-0.1.0/examples/07-profiles/stations/types/bench.yaml +13 -0
  174. litmus_test-0.1.0/examples/07-profiles/tests/test_rail.py +32 -0
  175. litmus_test-0.1.0/examples/07-profiles/tests/test_rail.yaml +16 -0
  176. litmus_test-0.1.0/examples/README.md +60 -0
  177. litmus_test-0.1.0/examples/interactive_station.py +385 -0
  178. litmus_test-0.1.0/examples/scripts/demo_duckdb.py +116 -0
  179. litmus_test-0.1.0/examples/scripts/demo_queries.sql +77 -0
  180. litmus_test-0.1.0/examples/scripts/query_results.py +264 -0
  181. litmus_test-0.1.0/examples/scripts/seed_artifact_demo.py +183 -0
  182. litmus_test-0.1.0/examples/static/station.css +94 -0
  183. litmus_test-0.1.0/pyproject.toml +187 -0
  184. litmus_test-0.1.0/scripts/build-skill-zip.py +182 -0
  185. litmus_test-0.1.0/scripts/fmt_yaml.py +57 -0
  186. litmus_test-0.1.0/src/litmus/__init__.py +25 -0
  187. litmus_test-0.1.0/src/litmus/analysis/__init__.py +9 -0
  188. litmus_test-0.1.0/src/litmus/analysis/_common.py +21 -0
  189. litmus_test-0.1.0/src/litmus/analysis/measurement_facets.py +272 -0
  190. litmus_test-0.1.0/src/litmus/analysis/measurements_query.py +847 -0
  191. litmus_test-0.1.0/src/litmus/analysis/metrics.py +389 -0
  192. litmus_test-0.1.0/src/litmus/analysis/runs_query.py +407 -0
  193. litmus_test-0.1.0/src/litmus/analysis/steps_query.py +345 -0
  194. litmus_test-0.1.0/src/litmus/api/__init__.py +6 -0
  195. litmus_test-0.1.0/src/litmus/api/_mime.py +71 -0
  196. litmus_test-0.1.0/src/litmus/api/app.py +943 -0
  197. litmus_test-0.1.0/src/litmus/api/dialogs/__init__.py +36 -0
  198. litmus_test-0.1.0/src/litmus/api/dialogs/manager.py +617 -0
  199. litmus_test-0.1.0/src/litmus/api/dialogs/models.py +79 -0
  200. litmus_test-0.1.0/src/litmus/api/models.py +79 -0
  201. litmus_test-0.1.0/src/litmus/api/responses.py +205 -0
  202. litmus_test-0.1.0/src/litmus/api/runner.py +220 -0
  203. litmus_test-0.1.0/src/litmus/api/schemas.py +347 -0
  204. litmus_test-0.1.0/src/litmus/catalog/__init__.py +6 -0
  205. litmus_test-0.1.0/src/litmus/catalog/generic/generic_dmm.yaml +61 -0
  206. litmus_test-0.1.0/src/litmus/catalog/generic/generic_eload.yaml +31 -0
  207. litmus_test-0.1.0/src/litmus/catalog/generic/generic_fgen.yaml +56 -0
  208. litmus_test-0.1.0/src/litmus/catalog/generic/generic_oscilloscope.yaml +45 -0
  209. litmus_test-0.1.0/src/litmus/catalog/generic/generic_psu.yaml +77 -0
  210. litmus_test-0.1.0/src/litmus/catalog/generic/generic_smu.yaml +73 -0
  211. litmus_test-0.1.0/src/litmus/cli.py +2589 -0
  212. litmus_test-0.1.0/src/litmus/client.py +438 -0
  213. litmus_test-0.1.0/src/litmus/connect.py +507 -0
  214. litmus_test-0.1.0/src/litmus/environment.py +84 -0
  215. litmus_test-0.1.0/src/litmus/execution/__init__.py +11 -0
  216. litmus_test-0.1.0/src/litmus/execution/_git.py +190 -0
  217. litmus_test-0.1.0/src/litmus/execution/_state.py +530 -0
  218. litmus_test-0.1.0/src/litmus/execution/accessors.py +54 -0
  219. litmus_test-0.1.0/src/litmus/execution/audit.py +51 -0
  220. litmus_test-0.1.0/src/litmus/execution/cascade.py +85 -0
  221. litmus_test-0.1.0/src/litmus/execution/connections.py +489 -0
  222. litmus_test-0.1.0/src/litmus/execution/dut_provider.py +201 -0
  223. litmus_test-0.1.0/src/litmus/execution/harness.py +1279 -0
  224. litmus_test-0.1.0/src/litmus/execution/instrument_events.py +42 -0
  225. litmus_test-0.1.0/src/litmus/execution/limits.py +154 -0
  226. litmus_test-0.1.0/src/litmus/execution/logger.py +1159 -0
  227. litmus_test-0.1.0/src/litmus/execution/metadata.py +102 -0
  228. litmus_test-0.1.0/src/litmus/execution/mocks.py +63 -0
  229. litmus_test-0.1.0/src/litmus/execution/profiles.py +612 -0
  230. litmus_test-0.1.0/src/litmus/execution/sidecar.py +280 -0
  231. litmus_test-0.1.0/src/litmus/execution/slot_runner.py +715 -0
  232. litmus_test-0.1.0/src/litmus/execution/slots.py +135 -0
  233. litmus_test-0.1.0/src/litmus/execution/sync.py +292 -0
  234. litmus_test-0.1.0/src/litmus/execution/vectors.py +205 -0
  235. litmus_test-0.1.0/src/litmus/execution/verify.py +218 -0
  236. litmus_test-0.1.0/src/litmus/expand.py +75 -0
  237. litmus_test-0.1.0/src/litmus/fixtures/__init__.py +6 -0
  238. litmus_test-0.1.0/src/litmus/fixtures/manager.py +357 -0
  239. litmus_test-0.1.0/src/litmus/grafana/__init__.py +1 -0
  240. litmus_test-0.1.0/src/litmus/grafana/bootstrap.py +61 -0
  241. litmus_test-0.1.0/src/litmus/grafana/cli.py +314 -0
  242. litmus_test-0.1.0/src/litmus/grafana/dashboards/asset_utilization.json +92 -0
  243. litmus_test-0.1.0/src/litmus/grafana/dashboards/channel_explorer.json +77 -0
  244. litmus_test-0.1.0/src/litmus/grafana/dashboards/event_log.json +92 -0
  245. litmus_test-0.1.0/src/litmus/grafana/dashboards/failure_pareto.json +104 -0
  246. litmus_test-0.1.0/src/litmus/grafana/dashboards/measurement_distribution.json +97 -0
  247. litmus_test-0.1.0/src/litmus/grafana/dashboards/measurement_trend.json +161 -0
  248. litmus_test-0.1.0/src/litmus/grafana/dashboards/station_comparison.json +95 -0
  249. litmus_test-0.1.0/src/litmus/grafana/dashboards/test_duration.json +104 -0
  250. litmus_test-0.1.0/src/litmus/grafana/dashboards/unit_traceability.json +73 -0
  251. litmus_test-0.1.0/src/litmus/grafana/dashboards/yield_overview.json +189 -0
  252. litmus_test-0.1.0/src/litmus/grafana/provisioning/dashboards.yaml.j2 +12 -0
  253. litmus_test-0.1.0/src/litmus/grafana/provisioning/datasources.yaml.j2 +17 -0
  254. litmus_test-0.1.0/src/litmus/grafana/server.py +254 -0
  255. litmus_test-0.1.0/src/litmus/init.py +670 -0
  256. litmus_test-0.1.0/src/litmus/instruments/__init__.py +10 -0
  257. litmus_test-0.1.0/src/litmus/instruments/base.py +82 -0
  258. litmus_test-0.1.0/src/litmus/instruments/discovery/__init__.py +62 -0
  259. litmus_test-0.1.0/src/litmus/instruments/discovery/_base.py +174 -0
  260. litmus_test-0.1.0/src/litmus/instruments/discovery/lxi.py +152 -0
  261. litmus_test-0.1.0/src/litmus/instruments/discovery/ni.py +95 -0
  262. litmus_test-0.1.0/src/litmus/instruments/discovery/serial.py +73 -0
  263. litmus_test-0.1.0/src/litmus/instruments/discovery/visa.py +98 -0
  264. litmus_test-0.1.0/src/litmus/instruments/lifecycle.py +213 -0
  265. litmus_test-0.1.0/src/litmus/instruments/loader.py +79 -0
  266. litmus_test-0.1.0/src/litmus/instruments/locks.py +116 -0
  267. litmus_test-0.1.0/src/litmus/instruments/mocks.py +220 -0
  268. litmus_test-0.1.0/src/litmus/instruments/observer.py +184 -0
  269. litmus_test-0.1.0/src/litmus/instruments/observers/__init__.py +36 -0
  270. litmus_test-0.1.0/src/litmus/instruments/observers/_base.py +68 -0
  271. litmus_test-0.1.0/src/litmus/instruments/observers/daqmx.py +70 -0
  272. litmus_test-0.1.0/src/litmus/instruments/observers/descriptor.py +69 -0
  273. litmus_test-0.1.0/src/litmus/instruments/observers/generic.py +92 -0
  274. litmus_test-0.1.0/src/litmus/instruments/observers/lantz.py +31 -0
  275. litmus_test-0.1.0/src/litmus/instruments/observers/modbus.py +96 -0
  276. litmus_test-0.1.0/src/litmus/instruments/observers/motion.py +68 -0
  277. litmus_test-0.1.0/src/litmus/instruments/observers/ni_modular.py +73 -0
  278. litmus_test-0.1.0/src/litmus/instruments/observers/ophyd.py +80 -0
  279. litmus_test-0.1.0/src/litmus/instruments/observers/pymeasure.py +120 -0
  280. litmus_test-0.1.0/src/litmus/instruments/observers/qcodes.py +66 -0
  281. litmus_test-0.1.0/src/litmus/instruments/observers/scpi.py +88 -0
  282. litmus_test-0.1.0/src/litmus/instruments/observers/tektronix.py +57 -0
  283. litmus_test-0.1.0/src/litmus/instruments/observers/visa.py +103 -0
  284. litmus_test-0.1.0/src/litmus/instruments/pool.py +295 -0
  285. litmus_test-0.1.0/src/litmus/instruments/proxy.py +59 -0
  286. litmus_test-0.1.0/src/litmus/instruments/route_manager.py +329 -0
  287. litmus_test-0.1.0/src/litmus/instruments/routed_proxy.py +88 -0
  288. litmus_test-0.1.0/src/litmus/instruments/server.py +373 -0
  289. litmus_test-0.1.0/src/litmus/instruments/switch.py +43 -0
  290. litmus_test-0.1.0/src/litmus/instruments/visa.py +294 -0
  291. litmus_test-0.1.0/src/litmus/matching/__init__.py +6 -0
  292. litmus_test-0.1.0/src/litmus/matching/service.py +969 -0
  293. litmus_test-0.1.0/src/litmus/mcp/__init__.py +6 -0
  294. litmus_test-0.1.0/src/litmus/mcp/server.py +649 -0
  295. litmus_test-0.1.0/src/litmus/mcp/tools.py +1650 -0
  296. litmus_test-0.1.0/src/litmus/models/__init__.py +18 -0
  297. litmus_test-0.1.0/src/litmus/models/capability.py +597 -0
  298. litmus_test-0.1.0/src/litmus/models/catalog.py +76 -0
  299. litmus_test-0.1.0/src/litmus/models/enums.py +334 -0
  300. litmus_test-0.1.0/src/litmus/models/instrument.py +149 -0
  301. litmus_test-0.1.0/src/litmus/models/instrument_asset.py +25 -0
  302. litmus_test-0.1.0/src/litmus/models/product.py +271 -0
  303. litmus_test-0.1.0/src/litmus/models/product_manifest.py +96 -0
  304. litmus_test-0.1.0/src/litmus/models/project.py +96 -0
  305. litmus_test-0.1.0/src/litmus/models/station.py +140 -0
  306. litmus_test-0.1.0/src/litmus/models/test_config.py +717 -0
  307. litmus_test-0.1.0/src/litmus/products/__init__.py +8 -0
  308. litmus_test-0.1.0/src/litmus/products/context.py +183 -0
  309. litmus_test-0.1.0/src/litmus/products/folder.py +251 -0
  310. litmus_test-0.1.0/src/litmus/products/loader.py +27 -0
  311. litmus_test-0.1.0/src/litmus/prompts/__init__.py +17 -0
  312. litmus_test-0.1.0/src/litmus/prompts/core.py +133 -0
  313. litmus_test-0.1.0/src/litmus/pytest_plugin/__init__.py +1210 -0
  314. litmus_test-0.1.0/src/litmus/pytest_plugin/autouse.py +360 -0
  315. litmus_test-0.1.0/src/litmus/pytest_plugin/helpers.py +348 -0
  316. litmus_test-0.1.0/src/litmus/pytest_plugin/hooks.py +1670 -0
  317. litmus_test-0.1.0/src/litmus/pytest_plugin/markers.py +203 -0
  318. litmus_test-0.1.0/src/litmus/pytest_plugin/retry.py +37 -0
  319. litmus_test-0.1.0/src/litmus/pytest_plugin/sweeps.py +117 -0
  320. litmus_test-0.1.0/src/litmus/reports/__init__.py +6 -0
  321. litmus_test-0.1.0/src/litmus/reports/core.py +421 -0
  322. litmus_test-0.1.0/src/litmus/reports/datasheet.py +831 -0
  323. litmus_test-0.1.0/src/litmus/reports/templates/datasheet.html +383 -0
  324. litmus_test-0.1.0/src/litmus/reports/templates/default.html +152 -0
  325. litmus_test-0.1.0/src/litmus/sbom.py +142 -0
  326. litmus_test-0.1.0/src/litmus/schema_export.py +101 -0
  327. litmus_test-0.1.0/src/litmus/signals.py +63 -0
  328. litmus_test-0.1.0/src/litmus/skills/SKILL.md +83 -0
  329. litmus_test-0.1.0/src/litmus/skills/agents/scaffold-writer.md +114 -0
  330. litmus_test-0.1.0/src/litmus/skills/agents/section-extractor.md +97 -0
  331. litmus_test-0.1.0/src/litmus/skills/agents/section-reviewer.md +152 -0
  332. litmus_test-0.1.0/src/litmus/skills/agents/section-splitter.md +88 -0
  333. litmus_test-0.1.0/src/litmus/skills/agents/section-writer.md +225 -0
  334. litmus_test-0.1.0/src/litmus/skills/catalog-scaffold.md +156 -0
  335. litmus_test-0.1.0/src/litmus/skills/commands/claude-code/catalog-from-datasheet.md +15 -0
  336. litmus_test-0.1.0/src/litmus/skills/commands/claude-code/process-catalog.md +42 -0
  337. litmus_test-0.1.0/src/litmus/skills/commands/copilot/catalog-from-datasheet.prompt.md +15 -0
  338. litmus_test-0.1.0/src/litmus/skills/commands/copilot/process-catalog.prompt.md +23 -0
  339. litmus_test-0.1.0/src/litmus/skills/refs/profiles.md +279 -0
  340. litmus_test-0.1.0/src/litmus/skills/templates/project-instructions.md +97 -0
  341. litmus_test-0.1.0/src/litmus/skills/workflow/datasheet-to-catalog.md +271 -0
  342. litmus_test-0.1.0/src/litmus/skills/workflow/datasheet-to-test.md +309 -0
  343. litmus_test-0.1.0/src/litmus/store.py +1603 -0
  344. litmus_test-0.1.0/src/litmus/ui/__init__.py +1 -0
  345. litmus_test-0.1.0/src/litmus/ui/_asgi.py +197 -0
  346. litmus_test-0.1.0/src/litmus/ui/app.py +17 -0
  347. litmus_test-0.1.0/src/litmus/ui/components/__init__.py +13 -0
  348. litmus_test-0.1.0/src/litmus/ui/components/artifact_viewer.py +248 -0
  349. litmus_test-0.1.0/src/litmus/ui/components/channel_values.py +88 -0
  350. litmus_test-0.1.0/src/litmus/ui/components/event_timeline.py +190 -0
  351. litmus_test-0.1.0/src/litmus/ui/components/execution_gantt.py +199 -0
  352. litmus_test-0.1.0/src/litmus/ui/components/instrument_activity.py +146 -0
  353. litmus_test-0.1.0/src/litmus/ui/components/session_table.py +118 -0
  354. litmus_test-0.1.0/src/litmus/ui/pages/__init__.py +22 -0
  355. litmus_test-0.1.0/src/litmus/ui/pages/channels/__init__.py +4 -0
  356. litmus_test-0.1.0/src/litmus/ui/pages/channels/detail.py +613 -0
  357. litmus_test-0.1.0/src/litmus/ui/pages/channels/list.py +283 -0
  358. litmus_test-0.1.0/src/litmus/ui/pages/dashboard.py +175 -0
  359. litmus_test-0.1.0/src/litmus/ui/pages/designer/__init__.py +5 -0
  360. litmus_test-0.1.0/src/litmus/ui/pages/designer/graph.py +635 -0
  361. litmus_test-0.1.0/src/litmus/ui/pages/designer/matching.py +663 -0
  362. litmus_test-0.1.0/src/litmus/ui/pages/designer/page.py +586 -0
  363. litmus_test-0.1.0/src/litmus/ui/pages/designer/properties.py +591 -0
  364. litmus_test-0.1.0/src/litmus/ui/pages/designer/state.py +468 -0
  365. litmus_test-0.1.0/src/litmus/ui/pages/docs/__init__.py +6 -0
  366. litmus_test-0.1.0/src/litmus/ui/pages/docs/index.py +101 -0
  367. litmus_test-0.1.0/src/litmus/ui/pages/docs/page.py +302 -0
  368. litmus_test-0.1.0/src/litmus/ui/pages/events/__init__.py +3 -0
  369. litmus_test-0.1.0/src/litmus/ui/pages/events/list.py +253 -0
  370. litmus_test-0.1.0/src/litmus/ui/pages/explore.py +854 -0
  371. litmus_test-0.1.0/src/litmus/ui/pages/fixtures/__init__.py +8 -0
  372. litmus_test-0.1.0/src/litmus/ui/pages/fixtures/detail.py +280 -0
  373. litmus_test-0.1.0/src/litmus/ui/pages/fixtures/edit.py +370 -0
  374. litmus_test-0.1.0/src/litmus/ui/pages/fixtures/list.py +104 -0
  375. litmus_test-0.1.0/src/litmus/ui/pages/fixtures/new.py +195 -0
  376. litmus_test-0.1.0/src/litmus/ui/pages/instruments/__init__.py +4 -0
  377. litmus_test-0.1.0/src/litmus/ui/pages/instruments/detail.py +320 -0
  378. litmus_test-0.1.0/src/litmus/ui/pages/instruments/edit.py +482 -0
  379. litmus_test-0.1.0/src/litmus/ui/pages/instruments/list.py +155 -0
  380. litmus_test-0.1.0/src/litmus/ui/pages/instruments/new.py +200 -0
  381. litmus_test-0.1.0/src/litmus/ui/pages/launch.py +178 -0
  382. litmus_test-0.1.0/src/litmus/ui/pages/live.py +102 -0
  383. litmus_test-0.1.0/src/litmus/ui/pages/metrics_page.py +1220 -0
  384. litmus_test-0.1.0/src/litmus/ui/pages/products/__init__.py +11 -0
  385. litmus_test-0.1.0/src/litmus/ui/pages/products/detail.py +204 -0
  386. litmus_test-0.1.0/src/litmus/ui/pages/products/edit.py +312 -0
  387. litmus_test-0.1.0/src/litmus/ui/pages/products/list.py +82 -0
  388. litmus_test-0.1.0/src/litmus/ui/pages/products/new.py +170 -0
  389. litmus_test-0.1.0/src/litmus/ui/pages/products/requirements.py +155 -0
  390. litmus_test-0.1.0/src/litmus/ui/pages/products/stations.py +203 -0
  391. litmus_test-0.1.0/src/litmus/ui/pages/results/__init__.py +4 -0
  392. litmus_test-0.1.0/src/litmus/ui/pages/results/detail.py +600 -0
  393. litmus_test-0.1.0/src/litmus/ui/pages/results/list.py +184 -0
  394. litmus_test-0.1.0/src/litmus/ui/pages/stations/__init__.py +4 -0
  395. litmus_test-0.1.0/src/litmus/ui/pages/stations/detail.py +252 -0
  396. litmus_test-0.1.0/src/litmus/ui/pages/stations/edit.py +273 -0
  397. litmus_test-0.1.0/src/litmus/ui/pages/stations/list.py +88 -0
  398. litmus_test-0.1.0/src/litmus/ui/pages/stations/new.py +311 -0
  399. litmus_test-0.1.0/src/litmus/ui/pages/tests/__init__.py +4 -0
  400. litmus_test-0.1.0/src/litmus/ui/pages/tests/list.py +37 -0
  401. litmus_test-0.1.0/src/litmus/ui/shared/__init__.py +8 -0
  402. litmus_test-0.1.0/src/litmus/ui/shared/components.py +1167 -0
  403. litmus_test-0.1.0/src/litmus/ui/shared/dialogs.py +105 -0
  404. litmus_test-0.1.0/src/litmus/ui/shared/event_binding.py +147 -0
  405. litmus_test-0.1.0/src/litmus/ui/shared/layout.py +200 -0
  406. litmus_test-0.1.0/src/litmus/ui/shared/services.py +910 -0
  407. litmus_test-0.1.0/src/litmus/ui/shared/timestamps.py +16 -0
  408. litmus_test-0.1.0/src/litmus/ui/static/global.css +381 -0
  409. litmus_test-0.1.0/src/litmus/utils/__init__.py +17 -0
  410. litmus_test-0.1.0/src/litmus/utils/enum_meta.py +800 -0
  411. litmus_test-0.1.0/src/litmus/utils/paths.py +75 -0
  412. litmus_test-0.1.0/src/litmus/utils/ranges.py +257 -0
  413. litmus_test-0.1.0/src/litmus/validation.py +82 -0
  414. litmus_test-0.1.0/tests/__init__.py +0 -0
  415. litmus_test-0.1.0/tests/conftest.py +65 -0
  416. litmus_test-0.1.0/tests/fixtures/specs/base_board.yaml +28 -0
  417. litmus_test-0.1.0/tests/fixtures/specs/base_board_with_driver.yaml +28 -0
  418. litmus_test-0.1.0/tests/fixtures/specs/circular_a.yaml +13 -0
  419. litmus_test-0.1.0/tests/fixtures/specs/circular_b.yaml +13 -0
  420. litmus_test-0.1.0/tests/fixtures/specs/power_board_v1.yaml +116 -0
  421. litmus_test-0.1.0/tests/fixtures/specs/variant_board.yaml +14 -0
  422. litmus_test-0.1.0/tests/fixtures/specs/variant_driver_inherit.yaml +3 -0
  423. litmus_test-0.1.0/tests/fixtures/specs/variant_driver_override.yaml +4 -0
  424. litmus_test-0.1.0/tests/fixtures/specs/variant_inherit_all.yaml +3 -0
  425. litmus_test-0.1.0/tests/test_api/test_mime_sniff.py +46 -0
  426. litmus_test-0.1.0/tests/test_api/test_ref_endpoint.py +210 -0
  427. litmus_test-0.1.0/tests/test_api/test_run_detail.py +152 -0
  428. litmus_test-0.1.0/tests/test_api/test_steps_endpoint.py +124 -0
  429. litmus_test-0.1.0/tests/test_catalog/__init__.py +0 -0
  430. litmus_test-0.1.0/tests/test_catalog/test_loader.py +439 -0
  431. litmus_test-0.1.0/tests/test_catalog/test_spec_bands.py +86 -0
  432. litmus_test-0.1.0/tests/test_config/__init__.py +1 -0
  433. litmus_test-0.1.0/tests/test_config/test_expand_helpers.py +255 -0
  434. litmus_test-0.1.0/tests/test_config/test_expander_wiring.py +67 -0
  435. litmus_test-0.1.0/tests/test_config/test_markers.py +202 -0
  436. litmus_test-0.1.0/tests/test_config/test_models.py +153 -0
  437. litmus_test-0.1.0/tests/test_config/test_station_compliance.py +101 -0
  438. litmus_test-0.1.0/tests/test_config/test_switch_route.py +81 -0
  439. litmus_test-0.1.0/tests/test_connect.py +238 -0
  440. litmus_test-0.1.0/tests/test_conventions.py +125 -0
  441. litmus_test-0.1.0/tests/test_data/__init__.py +1 -0
  442. litmus_test-0.1.0/tests/test_data/conftest.py +354 -0
  443. litmus_test-0.1.0/tests/test_data/test_atomic.py +67 -0
  444. litmus_test-0.1.0/tests/test_data/test_channel_server.py +266 -0
  445. litmus_test-0.1.0/tests/test_data/test_channel_store.py +370 -0
  446. litmus_test-0.1.0/tests/test_data/test_event_log.py +153 -0
  447. litmus_test-0.1.0/tests/test_data/test_event_reader.py +93 -0
  448. litmus_test-0.1.0/tests/test_data/test_event_store.py +137 -0
  449. litmus_test-0.1.0/tests/test_data/test_events.py +186 -0
  450. litmus_test-0.1.0/tests/test_data/test_export_atml.py +234 -0
  451. litmus_test-0.1.0/tests/test_data/test_export_hdf5.py +198 -0
  452. litmus_test-0.1.0/tests/test_data/test_export_mdf4.py +159 -0
  453. litmus_test-0.1.0/tests/test_data/test_export_stdf.py +208 -0
  454. litmus_test-0.1.0/tests/test_data/test_export_tdms.py +144 -0
  455. litmus_test-0.1.0/tests/test_data/test_instrument_arrays.py +266 -0
  456. litmus_test-0.1.0/tests/test_data/test_instrument_events.py +62 -0
  457. litmus_test-0.1.0/tests/test_data/test_materialize.py +181 -0
  458. litmus_test-0.1.0/tests/test_data/test_materializer.py +360 -0
  459. litmus_test-0.1.0/tests/test_data/test_measurement_writer.py +60 -0
  460. litmus_test-0.1.0/tests/test_data/test_models.py +213 -0
  461. litmus_test-0.1.0/tests/test_data/test_perf.py +205 -0
  462. litmus_test-0.1.0/tests/test_data/test_perf_daemon.py +242 -0
  463. litmus_test-0.1.0/tests/test_data/test_ref.py +98 -0
  464. litmus_test-0.1.0/tests/test_data/test_replay.py +155 -0
  465. litmus_test-0.1.0/tests/test_data/test_retention.py +119 -0
  466. litmus_test-0.1.0/tests/test_data/test_run_store.py +262 -0
  467. litmus_test-0.1.0/tests/test_data/test_runs_daemon_concurrency.py +178 -0
  468. litmus_test-0.1.0/tests/test_data/test_runs_duckdb_daemon_subscriber.py +337 -0
  469. litmus_test-0.1.0/tests/test_data/test_write_schema.py +193 -0
  470. litmus_test-0.1.0/tests/test_datasheet.py +309 -0
  471. litmus_test-0.1.0/tests/test_dialog_demo.py +92 -0
  472. litmus_test-0.1.0/tests/test_dialogs/__init__.py +0 -0
  473. litmus_test-0.1.0/tests/test_dialogs/test_dialog_events.py +144 -0
  474. litmus_test-0.1.0/tests/test_discovery/__init__.py +0 -0
  475. litmus_test-0.1.0/tests/test_discovery/test_models.py +32 -0
  476. litmus_test-0.1.0/tests/test_discovery/test_scanner.py +76 -0
  477. litmus_test-0.1.0/tests/test_dut_options.py +138 -0
  478. litmus_test-0.1.0/tests/test_e2e/test_examples.py +64 -0
  479. litmus_test-0.1.0/tests/test_e2e/test_spec_to_results.py +461 -0
  480. litmus_test-0.1.0/tests/test_e2e/test_workflows.py +265 -0
  481. litmus_test-0.1.0/tests/test_environment.py +60 -0
  482. litmus_test-0.1.0/tests/test_execution/__init__.py +1 -0
  483. litmus_test-0.1.0/tests/test_execution/test_class_keying.py +86 -0
  484. litmus_test-0.1.0/tests/test_execution/test_class_step_containers.py +643 -0
  485. litmus_test-0.1.0/tests/test_execution/test_connections_resolution.py +711 -0
  486. litmus_test-0.1.0/tests/test_execution/test_context_session_attrs.py +141 -0
  487. litmus_test-0.1.0/tests/test_execution/test_dut_provider.py +191 -0
  488. litmus_test-0.1.0/tests/test_execution/test_harness.py +853 -0
  489. litmus_test-0.1.0/tests/test_execution/test_harness_record.py +22 -0
  490. litmus_test-0.1.0/tests/test_execution/test_limits.py +317 -0
  491. litmus_test-0.1.0/tests/test_execution/test_logger.py +345 -0
  492. litmus_test-0.1.0/tests/test_execution/test_logger_record.py +65 -0
  493. litmus_test-0.1.0/tests/test_execution/test_markers_sidecar.py +393 -0
  494. litmus_test-0.1.0/tests/test_execution/test_multi_dut_e2e.py +179 -0
  495. litmus_test-0.1.0/tests/test_execution/test_outcome_cascade.py +177 -0
  496. litmus_test-0.1.0/tests/test_execution/test_phase_and_mocks.py +293 -0
  497. litmus_test-0.1.0/tests/test_execution/test_phase_wiring.py +205 -0
  498. litmus_test-0.1.0/tests/test_execution/test_product_resolution.py +112 -0
  499. litmus_test-0.1.0/tests/test_execution/test_profile_facets.py +303 -0
  500. litmus_test-0.1.0/tests/test_execution/test_prompt_fixture.py +284 -0
  501. litmus_test-0.1.0/tests/test_execution/test_pytest_native_plugin.py +670 -0
  502. litmus_test-0.1.0/tests/test_execution/test_required_inputs.py +187 -0
  503. litmus_test-0.1.0/tests/test_execution/test_slot_flag.py +193 -0
  504. litmus_test-0.1.0/tests/test_execution/test_slot_runner.py +185 -0
  505. litmus_test-0.1.0/tests/test_execution/test_slot_termination.py +169 -0
  506. litmus_test-0.1.0/tests/test_execution/test_slots.py +284 -0
  507. litmus_test-0.1.0/tests/test_execution/test_sync.py +237 -0
  508. litmus_test-0.1.0/tests/test_execution/test_vectors.py +257 -0
  509. litmus_test-0.1.0/tests/test_execution/test_vectors_fixture.py +285 -0
  510. litmus_test-0.1.0/tests/test_execution/test_verify_cascade.py +215 -0
  511. litmus_test-0.1.0/tests/test_execution/test_when_limits.py +334 -0
  512. litmus_test-0.1.0/tests/test_exporters.py +588 -0
  513. litmus_test-0.1.0/tests/test_fixtures/test_manager.py +208 -0
  514. litmus_test-0.1.0/tests/test_init.py +201 -0
  515. litmus_test-0.1.0/tests/test_instruments/__init__.py +0 -0
  516. litmus_test-0.1.0/tests/test_instruments/test_base.py +106 -0
  517. litmus_test-0.1.0/tests/test_instruments/test_discovery.py +186 -0
  518. litmus_test-0.1.0/tests/test_instruments/test_lifecycle.py +111 -0
  519. litmus_test-0.1.0/tests/test_instruments/test_loader.py +286 -0
  520. litmus_test-0.1.0/tests/test_instruments/test_locks.py +89 -0
  521. litmus_test-0.1.0/tests/test_instruments/test_lxi.py +97 -0
  522. litmus_test-0.1.0/tests/test_instruments/test_mocks.py +317 -0
  523. litmus_test-0.1.0/tests/test_instruments/test_models.py +217 -0
  524. litmus_test-0.1.0/tests/test_instruments/test_observer.py +109 -0
  525. litmus_test-0.1.0/tests/test_instruments/test_observers/__init__.py +0 -0
  526. litmus_test-0.1.0/tests/test_instruments/test_observers/conftest.py +44 -0
  527. litmus_test-0.1.0/tests/test_instruments/test_observers/test_daqmx.py +57 -0
  528. litmus_test-0.1.0/tests/test_instruments/test_observers/test_generic.py +98 -0
  529. litmus_test-0.1.0/tests/test_instruments/test_observers/test_lantz.py +71 -0
  530. litmus_test-0.1.0/tests/test_instruments/test_observers/test_modbus.py +60 -0
  531. litmus_test-0.1.0/tests/test_instruments/test_observers/test_motion.py +76 -0
  532. litmus_test-0.1.0/tests/test_instruments/test_observers/test_ni_modular.py +58 -0
  533. litmus_test-0.1.0/tests/test_instruments/test_observers/test_ophyd.py +80 -0
  534. litmus_test-0.1.0/tests/test_instruments/test_observers/test_pymeasure.py +226 -0
  535. litmus_test-0.1.0/tests/test_instruments/test_observers/test_qcodes.py +71 -0
  536. litmus_test-0.1.0/tests/test_instruments/test_observers/test_registry.py +128 -0
  537. litmus_test-0.1.0/tests/test_instruments/test_observers/test_scpi.py +50 -0
  538. litmus_test-0.1.0/tests/test_instruments/test_observers/test_tektronix.py +58 -0
  539. litmus_test-0.1.0/tests/test_instruments/test_observers/test_visa.py +98 -0
  540. litmus_test-0.1.0/tests/test_instruments/test_pool.py +118 -0
  541. litmus_test-0.1.0/tests/test_instruments/test_proxy.py +247 -0
  542. litmus_test-0.1.0/tests/test_instruments/test_route_manager.py +307 -0
  543. litmus_test-0.1.0/tests/test_instruments/test_routed_proxy.py +108 -0
  544. litmus_test-0.1.0/tests/test_instruments/test_server.py +515 -0
  545. litmus_test-0.1.0/tests/test_matching/__init__.py +1 -0
  546. litmus_test-0.1.0/tests/test_matching/test_channel_matching.py +642 -0
  547. litmus_test-0.1.0/tests/test_matching/test_conditions.py +525 -0
  548. litmus_test-0.1.0/tests/test_matching/test_recommend.py +396 -0
  549. litmus_test-0.1.0/tests/test_matching/test_service.py +531 -0
  550. litmus_test-0.1.0/tests/test_measurements_query/__init__.py +0 -0
  551. litmus_test-0.1.0/tests/test_measurements_query/test_measurement_facets.py +165 -0
  552. litmus_test-0.1.0/tests/test_measurements_query/test_measurements_query_sql.py +491 -0
  553. litmus_test-0.1.0/tests/test_plugin_fixtures.py +333 -0
  554. litmus_test-0.1.0/tests/test_products/test_loader.py +284 -0
  555. litmus_test-0.1.0/tests/test_products/test_models.py +175 -0
  556. litmus_test-0.1.0/tests/test_products/test_product_specband.py +89 -0
  557. litmus_test-0.1.0/tests/test_reports.py +278 -0
  558. litmus_test-0.1.0/tests/test_runs_query/__init__.py +0 -0
  559. litmus_test-0.1.0/tests/test_runs_query/test_runs_query.py +463 -0
  560. litmus_test-0.1.0/tests/test_sbom.py +151 -0
  561. litmus_test-0.1.0/tests/test_schemas.py +32 -0
  562. litmus_test-0.1.0/tests/test_signals.py +40 -0
  563. litmus_test-0.1.0/tests/test_steps_query/__init__.py +0 -0
  564. litmus_test-0.1.0/tests/test_steps_query/test_steps_query.py +273 -0
  565. litmus_test-0.1.0/tests/test_ui/__init__.py +0 -0
  566. litmus_test-0.1.0/tests/test_ui/test_subscribe_with_refresh.py +150 -0
  567. litmus_test-0.1.0/tests/test_utils/__init__.py +1 -0
  568. litmus_test-0.1.0/tests/test_utils/test_enum_meta.py +140 -0
  569. litmus_test-0.1.0/tests/test_utils/test_ranges.py +240 -0
  570. litmus_test-0.1.0/tests/test_yield/__init__.py +0 -0
  571. litmus_test-0.1.0/tests/test_yield/test_cli.py +179 -0
  572. litmus_test-0.1.0/tests/test_yield/test_metrics.py +250 -0
@@ -0,0 +1,29 @@
1
+ .venv/
2
+ __pycache__/
3
+ *.pyc
4
+ *.egg-info/
5
+ dist/
6
+ /data/
7
+ /examples/*/data/
8
+ .ruff_cache/
9
+ .pytest_cache/
10
+ .coverage
11
+ .claude/settings.local.json
12
+ .claude/scheduled_tasks.lock
13
+ .tmp/
14
+ examples/uv.lock
15
+ examples/*/uv.lock
16
+ dist/
17
+ .playwright-mcp/
18
+ report_*.html
19
+ *.mp4
20
+ *.pdf
21
+
22
+ # Generated skill refs (built from docs/ and models.py)
23
+ litmus/skills/refs/
24
+
25
+ # JSON schemas — regenerated via `litmus schema export` / `litmus init`
26
+ # / `litmus schema refresh`. The source of truth is the live Pydantic
27
+ # models in src/litmus/models/. Don't commit snapshots.
28
+ /schemas/
29
+ .benchmarks/
@@ -0,0 +1,39 @@
1
+ # Changelog
2
+
3
+ All notable changes to Litmus are documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and
6
+ this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ Pre-1.0 note: the public API is unstable. Breaking changes are possible in any
9
+ 0.x release and will be called out in this changelog.
10
+
11
+ ## [Unreleased]
12
+
13
+ ## [0.1.0] - 2026-04-15
14
+
15
+ Initial public release on PyPI as `litmus-test`.
16
+
17
+ ### Added
18
+
19
+ - `@litmus_test` decorator for pytest-native hardware tests with vector
20
+ expansion, limit checking, measurement recording, retries, and mock injection
21
+ - Station / fixture / product / sequence YAML configuration, loaded through a
22
+ single store layer with Pydantic validation
23
+ - Instrument fixtures resolved from station config (no `conftest.py`
24
+ boilerplate required)
25
+ - `--mock-instruments` mode for hardware-free development
26
+ - Parquet result storage with per-step instrument traceability
27
+ (serial, cal due date, firmware)
28
+ - DuckDB-backed analytics layer over the Parquet silver/gold layout
29
+ - Operator UI (`litmus serve`) built on NiceGUI
30
+ - FastAPI HTTP API and MCP server, with parity between the two
31
+ - Capability matching (`litmus_match`) against an instrument catalog
32
+ - CLI: `litmus init`, `discover`, `station init`, `new-test`, `serve`, `runs`,
33
+ `show`, `instrument list`, `mcp serve`, `setup`
34
+ - Optional extras for output formats (`stdf`, `hdf5`, `tdms`, `mdf4`),
35
+ transports (`s3`, `gcs`, `azure`, `sftp`), and integrations (`pymeasure`,
36
+ `ni`, `lxi`, `grafana`, `pdf`, `sbom`)
37
+
38
+ [Unreleased]: https://github.com/pragmatest-dev/litmus/compare/v0.1.0...HEAD
39
+ [0.1.0]: https://github.com/pragmatest-dev/litmus/releases/tag/v0.1.0
@@ -0,0 +1,619 @@
1
+ # Contributing to Litmus
2
+
3
+ This guide is for developers who want to contribute to Litmus itself. It provides a deep-dive into the architecture, key abstractions, and how the major systems interact.
4
+
5
+ ## Table of Contents
6
+
7
+ 1. [Architecture Overview](#architecture-overview)
8
+ 2. [Core Abstractions](#core-abstractions)
9
+ 3. [Data Flow](#data-flow)
10
+ 4. [Module Guide](#module-guide)
11
+ 5. [Extension Points](#extension-points)
12
+ 6. [Development Workflow](#development-workflow)
13
+
14
+ ---
15
+
16
+ ## Architecture Overview
17
+
18
+ Litmus is a **hardware test platform** organized into distinct subsystems:
19
+
20
+ ```
21
+ ┌─────────────────────────────────────────────────────────────────────┐
22
+ │ Test Execution │
23
+ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────────┐ │
24
+ │ │ pytest │───▶│ plugin │───▶│ fixtures (context, │ │
25
+ │ │ │ │ (hooks) │ │ verify, logger, spec) │ │
26
+ │ └─────────────┘ └─────────────┘ └─────────────────────────┘ │
27
+ │ │ │ │ │
28
+ │ ▼ ▼ ▼ │
29
+ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────────┐ │
30
+ │ │ Instruments│◀───│ Config │───▶│ Data Models │ │
31
+ │ │ (drivers) │ │ (YAML + │ │ (Measurement, │ │
32
+ │ │ │ │ Pydantic) │ │ TestRun, etc.) │ │
33
+ │ └─────────────┘ └─────────────┘ └─────────────────────────┘ │
34
+ │ │ │
35
+ │ ▼ │
36
+ │ ┌─────────────────────────┐ │
37
+ │ │ Storage Backend │ │
38
+ │ │ (Parquet files) │ │
39
+ │ └─────────────────────────┘ │
40
+ └─────────────────────────────────────────────────────────────────────┘
41
+
42
+ ┌─────────────────────────────────────────────────────────────────────┐
43
+ │ AI Integration │
44
+ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────────┐ │
45
+ │ │ MCP Server │ │ HTTP API │ │ Skills (prompts) │ │
46
+ │ │ (tools) │ │ (FastAPI) │ │ │ │
47
+ │ └─────────────┘ └─────────────┘ └─────────────────────────┘ │
48
+ └─────────────────────────────────────────────────────────────────────┘
49
+
50
+ ┌─────────────────────────────────────────────────────────────────────┐
51
+ │ Operator UI │
52
+ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────────┐ │
53
+ │ │ NiceGUI │ │ Dashboard │ │ Results Viewer │ │
54
+ │ │ (pages) │ │ + Launch │ │ │ │
55
+ │ └─────────────┘ └─────────────┘ └─────────────────────────┘ │
56
+ └─────────────────────────────────────────────────────────────────────┘
57
+ ```
58
+
59
+ ### Key Design Principles
60
+
61
+ 1. **Two Abstraction Levels**: Users get the simple pytest-native fixtures (`context`, `verify`, `logger`, `spec`). Test architects can construct a `TestHarness` directly for full control (non-pytest runners, custom flow).
62
+
63
+ 2. **Configuration-Driven**: Test behavior (vectors, limits, retries) lives in YAML, not code. This enables non-developers to modify tests.
64
+
65
+ 3. **Hierarchical Context**: Data flows through Run → Step → Vector scopes with inheritance.
66
+
67
+ 4. **AI-Ready, Not AI-Dependent**: We expose MCP tools and HTTP APIs for external agents, but the platform never calls LLMs itself.
68
+
69
+ ---
70
+
71
+ ## Core Abstractions
72
+
73
+ ### The Context Hierarchy
74
+
75
+ The `Context` class (`litmus/execution/harness.py`) is the user-facing API for test functions. It provides scoped inheritance:
76
+
77
+ ```
78
+ Run Context (session-wide metadata)
79
+
80
+ └── Step Context (per-test function)
81
+
82
+ └── Vector Context (per-parameter-set)
83
+ ```
84
+
85
+ **Key methods:**
86
+ ```python
87
+ class Context:
88
+ # Configure inputs (become in_* columns in Parquet)
89
+ def configure(key: str, value: Any) -> None
90
+ def set_in(key: str, value: Any) -> None
91
+ def get_param(key: str, default=None) -> Any
92
+
93
+ # Record observations (become out_* columns)
94
+ def observe(key: str, value: Any) -> None
95
+ def set_out(key: str, value: Any) -> None
96
+ def get_observation(key: str, default=None) -> Any
97
+
98
+ # Change detection for optimized loops
99
+ def changed(key: str) -> bool
100
+
101
+ # Access resolved limits
102
+ def get_limit(name: str) -> Limit | None
103
+
104
+ # Properties for bulk access
105
+ @property inputs -> dict[str, Any] # Merged with parent chain
106
+ @property outputs -> dict[str, Any] # Merged with parent chain
107
+ ```
108
+
109
+ **Parent chain lookup**: When you call `context.get_param("temperature")`, it searches the current context first, then walks up the parent chain (vector → step → run) until it finds a value.
110
+
111
+ ### Vector vs Context
112
+
113
+ Test vectors are defined in config and drive test looping. They are a subset of the context a test receives.
114
+
115
+ - **Vector** (`litmus/execution/vectors.py`): A dict subclass representing one parameter set from config. The harness expands and iterates over these internally.
116
+ - **Context** (`litmus/execution/harness.py`): The user-facing API that test functions receive. Contains vector params plus inherited run/step data, observations, and access to limits.
117
+
118
+ When using the pytest-native `context` fixture, you get a fully-populated `Context` for the active vector. When using `TestHarness` directly, you iterate `Vector` objects and must use `run_vector()` to construct each vector-level context (which auto-populates vector params into it).
119
+
120
+ ### TestHarness
121
+
122
+ The `TestHarness` class (`litmus/execution/harness.py`) is the core orchestration engine. It:
123
+
124
+ 1. **Expands vectors** from config (product, zip, nested, range)
125
+ 2. **Manages iteration** with `changed()` tracking across vectors
126
+ 3. **Handles retries** at the vector level
127
+ 4. **Resolves limits** from config, spec references, or callables
128
+ 5. **Records measurements** with automatic limit checking
129
+ 6. **Manages mock configuration** per-vector
130
+
131
+ **Key methods:**
132
+ ```python
133
+ class TestHarness:
134
+ # Properties
135
+ @property vectors -> list[Vector] # Expanded vectors
136
+ @property context -> Context # Current active context
137
+ @property current_vector -> Vector|None # During iteration
138
+
139
+ # Vector execution
140
+ @contextmanager
141
+ def run_vector(vector: Vector) -> Iterator[TestVector]
142
+
143
+ def run_with_retry(vector: Vector, test_fn: Callable) -> TestVector
144
+ def run_all(test_fn: Callable, step_name: str) -> TestStep
145
+
146
+ # Measurement
147
+ def measure(name: str, value: float, limit: Limit = None) -> Measurement
148
+
149
+ # Limit resolution (internal, but accessible via context.get_limit)
150
+ def _resolve_limit(name: str) -> Limit | None
151
+
152
+ # Prompts
153
+ def prompt(message: str, prompt_type: str = "confirm") -> Any
154
+ ```
155
+
156
+ ### pytest-native Fixtures
157
+
158
+ The pytest plugin (`litmus/pytest_plugin.py`) exposes the user-facing API
159
+ as a set of fixtures. A test function's signature declares which it wants:
160
+
161
+ - `context` — current-vector `Context` (params, observations, change tracking)
162
+ - `verify` — `verify(name, value)` shortcut that resolves a limit from sidecar
163
+ YAML / product spec and records a checked measurement
164
+ - `logger` — `TestRunLogger` for `measure(name, value, limit=...)` and
165
+ `record(...)` when you need full control
166
+ - `spec` — `SpecContext` bound to the session's product, for
167
+ `spec.check(char_name, value)` against product characteristics
168
+
169
+ Vector expansion is driven by `@pytest.mark.parametrize` and/or sidecar
170
+ `vectors:` blocks in `test_<name>.yaml`. Limit resolution chain is:
171
+ explicit `limit=` → sidecar `limits:` entry (flat or condition-indexed
172
+ via `when:`) → active product spec → unchecked.
173
+
174
+ ### Data Models
175
+
176
+ The result hierarchy (`litmus/data/models.py`):
177
+
178
+ ```
179
+ TestRun
180
+ ├── id, started_at, ended_at
181
+ ├── dut: DUT (serial, part_number, revision)
182
+ ├── station_id, operator_id, etc.
183
+ ├── outcome: Outcome (PASS/FAIL/ERROR/SKIP)
184
+ └── steps: list[TestStep]
185
+ ├── name, description
186
+ ├── outcome
187
+ └── vectors: list[TestVector]
188
+ ├── index, params (in_*)
189
+ ├── observations (out_*)
190
+ ├── outcome
191
+ └── measurements: list[Measurement]
192
+ ├── name, value, units
193
+ ├── low_limit, high_limit, nominal
194
+ ├── outcome
195
+ └── spec_ref (traceability)
196
+ ```
197
+
198
+ **Key model: `Measurement`**
199
+ ```python
200
+ class Measurement:
201
+ name: str
202
+ value: float | None
203
+ units: str | None
204
+ low_limit: float | None
205
+ high_limit: float | None
206
+ nominal: float | None
207
+ outcome: Outcome | None
208
+ spec_ref: str | None # Human-readable spec reference
209
+ dut_pin: str | None # DUT pin measured
210
+ instrument_channel: str | None # Instrument channel used
211
+
212
+ def check_limit() -> Outcome # Evaluates value against limits
213
+ ```
214
+
215
+ ### Limit Resolution
216
+
217
+ Limits can come from multiple sources. Resolution order in `TestHarness._resolve_limit()`:
218
+
219
+ 1. **Direct Limit object** in config
220
+ 2. **MeasurementLimitConfig** with direct values (low/high/nominal)
221
+ 3. **Spec reference** → resolves via `SpecContext`
222
+ 4. **Callable** → Python function or inline code evaluated with context
223
+ 5. **SpecContext lookup** → characteristic name matches measurement name
224
+
225
+ **Callable limits** enable dynamic limits based on current vector:
226
+ ```yaml
227
+ limits:
228
+ output_voltage:
229
+ callable: "Limit(low=ctx.get_param('vin') * 0.65, high=ctx.get_param('vin') * 0.68, units='V')"
230
+ ```
231
+
232
+ ### SpecContext (Spec-Driven Testing)
233
+
234
+ `SpecContext` (`litmus/products/context.py`) bridges product specifications and test execution:
235
+
236
+ ```python
237
+ class SpecContext:
238
+ product: Product # Loaded product spec
239
+ fixture: FixtureConfig|None # Fixture routing
240
+ default_guardband_pct: float # Default tightening
241
+
242
+ def get_limit(char_id: str, guardband_pct=None, **conditions) -> Limit
243
+ def get_characteristic(char_id: str) -> Characteristic
244
+ def get_pin_info(char_id: str) -> dict # For traceability
245
+ ```
246
+
247
+ **Limit derivation** handles:
248
+ - Condition matching (temperature, load, etc.)
249
+ - Guardband application (tightens limits by %)
250
+ - Spec reference generation for traceability
251
+
252
+ ---
253
+
254
+ ## Data Flow
255
+
256
+ ### Test Execution Flow
257
+
258
+ ```
259
+ 1. pytest starts
260
+ └── pytest_configure() registers markers
261
+
262
+ 2. Session starts
263
+ ├── logger fixture creates TestRunLogger
264
+ ├── instruments fixture connects to hardware (or mocks)
265
+ └── spec_context fixture loads product spec
266
+
267
+ 3. Each parametrize case / vector
268
+ ├── pytest_runtest_call opens a logger step
269
+ ├── context / verify / logger / spec fixtures resolve
270
+ ├── Active vector params pushed into ContextVars
271
+ ├── Test body runs — verify(name, value) calls:
272
+ │ ├── Resolve limit (sidecar → product spec → unchecked)
273
+ │ ├── logger.measure() creates Measurement
274
+ │ ├── measurement.check_limit()
275
+ │ └── Append to current TestVector
276
+
277
+ └── pytest_runtest_call closes the step; outcome rolls up
278
+
279
+ 4. Session ends
280
+ ├── logger.finalize() completes TestRun
281
+ └── ParquetBackend.save_test_run() writes results
282
+ ```
283
+
284
+ ### Measurement Recording Flow
285
+
286
+ ```
287
+ Test function returns value(s)
288
+
289
+
290
+ TestHarness._record_result()
291
+
292
+ ├── dict → multiple measurements
293
+ ├── tuple → named measurement
294
+ └── value → measurement with step name
295
+
296
+
297
+ TestHarness.measure()
298
+
299
+ ├── Resolve limit (_resolve_limit)
300
+ ├── Create Measurement object
301
+ ├── measurement.check_limit()
302
+ └── Append to current TestVector
303
+ ```
304
+
305
+ ### Context Inheritance Flow
306
+
307
+ ```
308
+ Context created for vector
309
+
310
+ ├── Parent = step context (or run context)
311
+ ├── Prev = previous vector context (for changed())
312
+ └── Harness = TestHarness reference (for get_limit())
313
+
314
+
315
+ Vector params → context._inputs
316
+
317
+
318
+ context.get_param("key") checks:
319
+ 1. This context._inputs
320
+ 2. Parent context._inputs (recursive)
321
+ 3. Return default
322
+ ```
323
+
324
+ ---
325
+
326
+ ## Module Guide
327
+
328
+ ### litmus/execution/
329
+
330
+ The core test execution engine.
331
+
332
+ | File | Purpose |
333
+ |------|---------|
334
+ | `plugin.py` | pytest plugin - fixtures, hooks, CLI options |
335
+ | `harness.py` | TestHarness + Context classes |
336
+ | `vectors.py` | Vector expansion (product, zip, nested, range) |
337
+ | `decorators.py` | @measure decorator + current-logger ContextVar helpers |
338
+ | `logger.py` | TestRunLogger for accumulating results |
339
+ | `runner.py` | Async subprocess runner for UI |
340
+
341
+ **Entry point**: `plugin.py` registers with pytest and provides fixtures that create harnesses and loggers.
342
+
343
+ ### litmus/config/
344
+
345
+ Configuration models and loading.
346
+
347
+ | File | Purpose |
348
+ |------|---------|
349
+ | `models.py` | Pydantic models: Limit, Specification, RetryConfig, etc. |
350
+ | `loader.py` | YAML loading and test config resolution |
351
+
352
+ **Key models**:
353
+ - `Limit` - Test limit with units and spec reference
354
+ - `MeasurementLimitConfig` - Flexible limit configuration (direct, ref, callable)
355
+ - `RetryConfig` - Retry behavior settings
356
+ - `VectorConfig` - Vector expansion configuration
357
+
358
+ ### litmus/data/
359
+
360
+ Data models and storage backends.
361
+
362
+ | File | Purpose |
363
+ |------|---------|
364
+ | `models.py` | TestRun, TestStep, TestVector, Measurement, Outcome |
365
+ | `backends/parquet.py` | Parquet file storage |
366
+
367
+ **Parquet schema**: Results are flattened to rows per measurement with `in_*` and `out_*` columns for context.
368
+
369
+ ### litmus/instruments/
370
+
371
+ Instrument drivers and mocks.
372
+
373
+ | File | Purpose |
374
+ |------|---------|
375
+ | `base.py` | Abstract Instrument base class |
376
+ | `visa.py` | VisaInstrument for SCPI instruments |
377
+ | `dmm.py`, `psu.py`, `eload.py`, `scope.py` | Concrete drivers |
378
+ | `mocks.py` | Generic Mock factory |
379
+
380
+ **Mock system**: `Mock(DMM, measure_voltage=3.3)` creates a mock that inherits from DMM, passes isinstance checks, and returns configured values.
381
+
382
+ ### litmus/products/
383
+
384
+ Product specification system.
385
+
386
+ | File | Purpose |
387
+ |------|---------|
388
+ | `models.py` | Product, Characteristic, Pin, TestRequirement |
389
+ | `context.py` | SpecContext for spec-driven testing |
390
+ | `loader.py` | YAML loading for product specs |
391
+ | `limits.py` | derive_limit() function |
392
+
393
+ ### litmus/mcp/
394
+
395
+ MCP server for AI integration.
396
+
397
+ | File | Purpose |
398
+ |------|---------|
399
+ | `server.py` | FastMCP server definition |
400
+ | `tools.py` | Tool implementations (litmus, discover, match, run, open) |
401
+
402
+ ### litmus/ui/
403
+
404
+ NiceGUI operator interface.
405
+
406
+ | Directory | Purpose |
407
+ |-----------|---------|
408
+ | `pages/` | Dashboard, launch, results, live views |
409
+ | `shared/` | Layout, components, dialogs |
410
+ | `static/` | CSS assets |
411
+
412
+ ### litmus/api/
413
+
414
+ HTTP API endpoints.
415
+
416
+ | File | Purpose |
417
+ |------|---------|
418
+ | `app.py` | FastAPI + NiceGUI app factory |
419
+ | `models.py` | API request/response models |
420
+
421
+ ---
422
+
423
+ ## Extension Points
424
+
425
+ ### Adding a New Instrument Driver
426
+
427
+ 1. Create `litmus/instruments/new_instrument.py`:
428
+ ```python
429
+ from litmus.instruments.visa import VisaInstrument
430
+
431
+ class NewInstrument(VisaInstrument):
432
+ def measure_something(self) -> float:
433
+ return float(self.query("MEAS:SOMETHING?"))
434
+
435
+ def set_something(self, value: float) -> None:
436
+ self.write(f"SOMETHING {value}")
437
+ ```
438
+
439
+ 2. Register SCPI mapping for mocks in `mocks.py`:
440
+ ```python
441
+ _register_scpi_mapping(
442
+ NewInstrument,
443
+ {
444
+ "measure_something": ["MEAS:SOMETHING?"],
445
+ "something": ["MEAS:SOMETHING?"], # Alias
446
+ },
447
+ )
448
+ ```
449
+
450
+ 3. Add to driver lookup in `plugin.py`:
451
+ ```python
452
+ def _get_driver_class(instrument_type: str):
453
+ from litmus.instruments import NewInstrument
454
+ drivers = {
455
+ # ...
456
+ "new_instrument": NewInstrument,
457
+ }
458
+ ```
459
+
460
+ ### Adding a New Vector Expansion Mode
461
+
462
+ 1. Add expansion function in `vectors.py`:
463
+ ```python
464
+ def expand_custom(config: dict) -> list[Vector]:
465
+ # Your expansion logic
466
+ result = []
467
+ for i, params in enumerate(your_expansion):
468
+ v = Vector(params)
469
+ v["_index"] = i
470
+ if i > 0:
471
+ v["_prev"] = result[i - 1]
472
+ result.append(v)
473
+ return result
474
+ ```
475
+
476
+ 2. Register in `expand_vectors()`:
477
+ ```python
478
+ if expand_mode == "custom":
479
+ return expand_custom(config)
480
+ ```
481
+
482
+ ### Adding a New Storage Backend
483
+
484
+ 1. Create `litmus/data/backends/new_backend.py`:
485
+ ```python
486
+ class NewBackend:
487
+ def save_test_run(self, test_run: TestRun) -> str:
488
+ # Save and return ID/path
489
+ pass
490
+
491
+ def load_test_run(self, run_id: str) -> TestRun:
492
+ pass
493
+
494
+ def list_runs(self, limit: int = 100) -> list[dict]:
495
+ pass
496
+ ```
497
+
498
+ 2. Use your backend in plugin.py or configure it via settings.
499
+
500
+ ### Adding MCP Tools
501
+
502
+ 1. Add tool implementation in `mcp/tools.py`:
503
+ ```python
504
+ def new_tool_impl(arg1: str, arg2: int) -> dict[str, Any]:
505
+ # Implementation
506
+ return {"result": "..."}
507
+ ```
508
+
509
+ 2. Register in `mcp/server.py`:
510
+ ```python
511
+ @mcp.tool(name="litmus_new")
512
+ def new_tool(arg1: str, arg2: int) -> dict[str, Any]:
513
+ """Tool description for AI agents."""
514
+ return new_tool_impl(arg1, arg2)
515
+ ```
516
+
517
+ ---
518
+
519
+ ## Development Workflow
520
+
521
+ ### Setup
522
+
523
+ ```bash
524
+ # Clone and install with all optional extras (pyright needs these installed
525
+ # to resolve imports for the exporters, transports, grafana, etc.)
526
+ git clone <repo>
527
+ cd litmus
528
+ uv sync --all-extras
529
+
530
+ # Install the pre-commit hooks once per clone. Hooks run ruff check,
531
+ # ruff format, pyright, and a handful of safety checks on every commit.
532
+ uv run pre-commit install
533
+
534
+ # Run tests
535
+ pytest
536
+
537
+ # Run with coverage
538
+ pytest --cov=litmus
539
+
540
+ # Lint, format, type-check (all run by the pre-commit hook too)
541
+ uv run ruff check .
542
+ uv run ruff format .
543
+ uv run pyright
544
+
545
+ # Run all pre-commit hooks manually across the repo
546
+ uv run pre-commit run --all-files
547
+ ```
548
+
549
+ ### Testing Your Changes
550
+
551
+ **Unit tests**: Add to `tests/` directory
552
+ ```bash
553
+ pytest tests/test_your_feature.py -v
554
+ ```
555
+
556
+ **Demo tests** (with mock instruments):
557
+ ```bash
558
+ cd demo
559
+ pytest tests/ --station=demo_station_001 --mock-instruments -v
560
+ ```
561
+
562
+ **Integration testing**:
563
+ ```bash
564
+ # Start UI
565
+ litmus serve --reload
566
+
567
+ # Run MCP server
568
+ litmus mcp serve
569
+ ```
570
+
571
+ ### Code Style Guidelines
572
+
573
+ 1. **Pydantic for config/data models**: All configuration and result structures use Pydantic
574
+ 2. **Type hints everywhere**: Use type annotations, especially for public APIs
575
+ 3. **Docstrings**: Google style, with Args/Returns/Raises sections
576
+ 4. **No magic**: Prefer explicit over implicit. Configuration should be visible.
577
+ 5. **YAML for config**: Human-editable configuration stays in YAML files
578
+
579
+ ### Common Patterns
580
+
581
+ **Context manager for resources**:
582
+ ```python
583
+ with harness.run_vector(vector) as tv:
584
+ # Vector execution
585
+ harness.measure("name", value)
586
+ ```
587
+
588
+ **Limit resolution with fallback**:
589
+ ```python
590
+ limit = context.get_limit("measurement_name")
591
+ if limit:
592
+ # Use limit
593
+ else:
594
+ # No limit configured
595
+ ```
596
+
597
+ **Mock value configuration**:
598
+ ```python
599
+ # Per-vector in config.yaml
600
+ vectors:
601
+ - vin: 5.0
602
+ _mocks:
603
+ dmm.measure_voltage: 3.3
604
+ ```
605
+
606
+ ### Debugging Tips
607
+
608
+ 1. **Check vector expansion**: Print `harness.vectors` to see expanded params
609
+ 2. **Trace limit resolution**: Add logging to `_resolve_limit()`
610
+ 3. **Mock behavior**: Check `mock.mock_write_log` for SCPI commands sent
611
+ 4. **Context inheritance**: Print `context.params` at each level
612
+
613
+ ---
614
+
615
+ ## Questions?
616
+
617
+ - Check existing tests in `tests/` for usage examples
618
+ - The `examples/` directory has complete working examples (three tiers: `01-bringup`, `02-station`, `03-profiles`)
619
+ - Open an issue for design questions