testerkit 0.4.0__py3-none-any.whl

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 (521) hide show
  1. testerkit/__init__.py +68 -0
  2. testerkit/_docs/_assets/operator-ui/channels/table.png +0 -0
  3. testerkit/_docs/_assets/operator-ui/dashboard/runs.png +0 -0
  4. testerkit/_docs/_assets/operator-ui/dashboard/stations.png +0 -0
  5. testerkit/_docs/_assets/operator-ui/events/filters.png +0 -0
  6. testerkit/_docs/_assets/operator-ui/events/table.png +0 -0
  7. testerkit/_docs/_assets/operator-ui/explore/chart.png +0 -0
  8. testerkit/_docs/_assets/operator-ui/explore/filters.png +0 -0
  9. testerkit/_docs/_assets/operator-ui/explore/plot-controls.png +0 -0
  10. testerkit/_docs/_assets/operator-ui/files/table.png +0 -0
  11. testerkit/_docs/_assets/operator-ui/launch/form.png +0 -0
  12. testerkit/_docs/_assets/operator-ui/metrics/filters.png +0 -0
  13. testerkit/_docs/_assets/operator-ui/metrics/pareto.png +0 -0
  14. testerkit/_docs/_assets/operator-ui/metrics/ppk.png +0 -0
  15. testerkit/_docs/_assets/operator-ui/metrics/yield.png +0 -0
  16. testerkit/_docs/_assets/operator-ui/results/detail-header.png +0 -0
  17. testerkit/_docs/_assets/operator-ui/results/detail-measurements.png +0 -0
  18. testerkit/_docs/_assets/operator-ui/results/detail-overview.png +0 -0
  19. testerkit/_docs/_assets/operator-ui/results/detail-steps.png +0 -0
  20. testerkit/_docs/_assets/operator-ui/results/stats.png +0 -0
  21. testerkit/_docs/_assets/operator-ui/results/table.png +0 -0
  22. testerkit/_docs/_assets/operator-ui/tour/designer.png +0 -0
  23. testerkit/_docs/_assets/operator-ui/tour/docs.png +0 -0
  24. testerkit/_docs/_assets/operator-ui/tour/fixtures.png +0 -0
  25. testerkit/_docs/_assets/operator-ui/tour/instruments.png +0 -0
  26. testerkit/_docs/_assets/operator-ui/tour/parts.png +0 -0
  27. testerkit/_docs/_assets/operator-ui/tour/profiles.png +0 -0
  28. testerkit/_docs/_assets/operator-ui/tour/stations.png +0 -0
  29. testerkit/_docs/_assets/operator-ui/tour/tests.png +0 -0
  30. testerkit/_docs/_assets/operator-ui/tour/uuts.png +0 -0
  31. testerkit/_docs/concepts/configuration/capabilities.md +447 -0
  32. testerkit/_docs/concepts/configuration/fixtures.md +335 -0
  33. testerkit/_docs/concepts/configuration/index.md +14 -0
  34. testerkit/_docs/concepts/configuration/parts.md +276 -0
  35. testerkit/_docs/concepts/configuration/stations.md +214 -0
  36. testerkit/_docs/concepts/data/data-stores.md +199 -0
  37. testerkit/_docs/concepts/data/event-log.md +185 -0
  38. testerkit/_docs/concepts/data/event-sourcing.md +76 -0
  39. testerkit/_docs/concepts/data/flight-streaming.md +58 -0
  40. testerkit/_docs/concepts/data/index.md +17 -0
  41. testerkit/_docs/concepts/data/sessions.md +78 -0
  42. testerkit/_docs/concepts/data/three-verbs.md +260 -0
  43. testerkit/_docs/concepts/execution/index.md +13 -0
  44. testerkit/_docs/concepts/execution/outcomes.md +199 -0
  45. testerkit/_docs/concepts/execution/step-hierarchy.md +170 -0
  46. testerkit/_docs/concepts/execution/step-manifest.md +115 -0
  47. testerkit/_docs/concepts/index.md +40 -0
  48. testerkit/_docs/concepts/overview/ai-integration.md +57 -0
  49. testerkit/_docs/concepts/overview/architecture.md +357 -0
  50. testerkit/_docs/concepts/overview/index.md +14 -0
  51. testerkit/_docs/concepts/overview/platform-vs-framework.md +147 -0
  52. testerkit/_docs/concepts/overview/pytest.md +53 -0
  53. testerkit/_docs/concepts/overview/tiers.md +46 -0
  54. testerkit/_docs/how-to/catalog/datasheet-to-test.md +169 -0
  55. testerkit/_docs/how-to/catalog/index.md +11 -0
  56. testerkit/_docs/how-to/configuration/configuring-stations.md +364 -0
  57. testerkit/_docs/how-to/configuration/custom-drivers.md +484 -0
  58. testerkit/_docs/how-to/configuration/index.md +13 -0
  59. testerkit/_docs/how-to/configuration/mock-mode.md +342 -0
  60. testerkit/_docs/how-to/data/benchmarking.md +120 -0
  61. testerkit/_docs/how-to/data/capture-an-artifact.md +84 -0
  62. testerkit/_docs/how-to/data/capture-waveform.md +110 -0
  63. testerkit/_docs/how-to/data/choosing-a-channel-verb.md +108 -0
  64. testerkit/_docs/how-to/data/compare-runs.md +120 -0
  65. testerkit/_docs/how-to/data/export-results.md +104 -0
  66. testerkit/_docs/how-to/data/find-flaky-tests.md +108 -0
  67. testerkit/_docs/how-to/data/grafana-dashboards.md +148 -0
  68. testerkit/_docs/how-to/data/index.md +23 -0
  69. testerkit/_docs/how-to/data/mcp-debug-failures.md +125 -0
  70. testerkit/_docs/how-to/data/mcp-query-runs.md +120 -0
  71. testerkit/_docs/how-to/data/querying-channels.md +117 -0
  72. testerkit/_docs/how-to/data/querying-events.md +85 -0
  73. testerkit/_docs/how-to/data/stream-live-channel.md +134 -0
  74. testerkit/_docs/how-to/execution/custom-operator-ui.md +136 -0
  75. testerkit/_docs/how-to/execution/index.md +21 -0
  76. testerkit/_docs/how-to/execution/limits.md +193 -0
  77. testerkit/_docs/how-to/execution/managing-sessions.md +100 -0
  78. testerkit/_docs/how-to/execution/multi-uut-testing.md +211 -0
  79. testerkit/_docs/how-to/execution/operator-prompts.md +151 -0
  80. testerkit/_docs/how-to/execution/profiles.md +304 -0
  81. testerkit/_docs/how-to/execution/spec-driven-testing.md +169 -0
  82. testerkit/_docs/how-to/execution/test-context.md +212 -0
  83. testerkit/_docs/how-to/execution/traceability.md +252 -0
  84. testerkit/_docs/how-to/execution/vector-expansion.md +394 -0
  85. testerkit/_docs/how-to/execution/writing-tests.md +363 -0
  86. testerkit/_docs/how-to/index.md +59 -0
  87. testerkit/_docs/how-to/overview/index.md +11 -0
  88. testerkit/_docs/how-to/overview/mcp-integration.md +168 -0
  89. testerkit/_docs/how-to/overview/operator-ui-tour.md +234 -0
  90. testerkit/_docs/integration/data/grafana.md +162 -0
  91. testerkit/_docs/integration/data/index.md +15 -0
  92. testerkit/_docs/integration/data/lakehouse-import.md +226 -0
  93. testerkit/_docs/integration/data/logging.md +131 -0
  94. testerkit/_docs/integration/data/results-api.md +51 -0
  95. testerkit/_docs/integration/index.md +20 -0
  96. testerkit/_docs/integration/runtime/harness.md +281 -0
  97. testerkit/_docs/integration/runtime/index.md +13 -0
  98. testerkit/_docs/integration/runtime/instruments.md +284 -0
  99. testerkit/_docs/integration/runtime/pytest-existing.md +315 -0
  100. testerkit/_docs/reference/catalog/cookbook.md +389 -0
  101. testerkit/_docs/reference/catalog/index.md +11 -0
  102. testerkit/_docs/reference/catalog/schema.md +375 -0
  103. testerkit/_docs/reference/cli.md +660 -0
  104. testerkit/_docs/reference/configuration.md +406 -0
  105. testerkit/_docs/reference/data/channels-schema.md +145 -0
  106. testerkit/_docs/reference/data/event-types.md +554 -0
  107. testerkit/_docs/reference/data/events-schema.md +120 -0
  108. testerkit/_docs/reference/data/files-schema.md +141 -0
  109. testerkit/_docs/reference/data/index.md +18 -0
  110. testerkit/_docs/reference/data/models.md +2229 -0
  111. testerkit/_docs/reference/data/outputs.md +82 -0
  112. testerkit/_docs/reference/data/parquet-schema.md +529 -0
  113. testerkit/_docs/reference/data/performance-limits.md +224 -0
  114. testerkit/_docs/reference/data/query-api.md +247 -0
  115. testerkit/_docs/reference/index.md +69 -0
  116. testerkit/_docs/reference/operator-ui/channels/detail.md +82 -0
  117. testerkit/_docs/reference/operator-ui/channels/list.md +70 -0
  118. testerkit/_docs/reference/operator-ui/dashboard.md +87 -0
  119. testerkit/_docs/reference/operator-ui/designer.md +167 -0
  120. testerkit/_docs/reference/operator-ui/events.md +89 -0
  121. testerkit/_docs/reference/operator-ui/files.md +74 -0
  122. testerkit/_docs/reference/operator-ui/fixtures.md +100 -0
  123. testerkit/_docs/reference/operator-ui/instruments.md +126 -0
  124. testerkit/_docs/reference/operator-ui/launch.md +79 -0
  125. testerkit/_docs/reference/operator-ui/live.md +102 -0
  126. testerkit/_docs/reference/operator-ui/measurements.md +159 -0
  127. testerkit/_docs/reference/operator-ui/metrics.md +217 -0
  128. testerkit/_docs/reference/operator-ui/parts.md +98 -0
  129. testerkit/_docs/reference/operator-ui/profiles.md +103 -0
  130. testerkit/_docs/reference/operator-ui/results/detail.md +214 -0
  131. testerkit/_docs/reference/operator-ui/results/list.md +106 -0
  132. testerkit/_docs/reference/operator-ui/stations.md +98 -0
  133. testerkit/_docs/reference/operator-ui/tests.md +173 -0
  134. testerkit/_docs/reference/operator-ui/uuts.md +67 -0
  135. testerkit/_docs/reference/overview/index.md +11 -0
  136. testerkit/_docs/reference/overview/pytest-native.md +142 -0
  137. testerkit/_docs/reference/overview/skills.md +177 -0
  138. testerkit/_docs/reference/pytest/fixtures.md +342 -0
  139. testerkit/_docs/reference/pytest/index.md +12 -0
  140. testerkit/_docs/reference/pytest/markers.md +257 -0
  141. testerkit/_docs/reference/runtime/api.md +246 -0
  142. testerkit/_docs/reference/runtime/client.md +280 -0
  143. testerkit/_docs/reference/runtime/connect.md +140 -0
  144. testerkit/_docs/reference/runtime/index.md +13 -0
  145. testerkit/_docs/tutorial/01-first-test.md +113 -0
  146. testerkit/_docs/tutorial/02-mock-instruments.md +88 -0
  147. testerkit/_docs/tutorial/03-fixtures.md +169 -0
  148. testerkit/_docs/tutorial/04-limits.md +139 -0
  149. testerkit/_docs/tutorial/05-configuration.md +194 -0
  150. testerkit/_docs/tutorial/06-specifications.md +273 -0
  151. testerkit/_docs/tutorial/07-real-instruments.md +245 -0
  152. testerkit/_docs/tutorial/08-capabilities.md +223 -0
  153. testerkit/_docs/tutorial/09-production.md +374 -0
  154. testerkit/_docs/tutorial/10-live-monitoring.md +156 -0
  155. testerkit/_docs/tutorial/11-waveforms-and-evidence.md +169 -0
  156. testerkit/_docs/tutorial/12-continuous-monitoring.md +145 -0
  157. testerkit/_docs/tutorial/13-parallel-testing.md +233 -0
  158. testerkit/_docs/tutorial/index.md +61 -0
  159. testerkit/_docs/tutorial/quickstart.md +282 -0
  160. testerkit/analysis/__init__.py +9 -0
  161. testerkit/analysis/_common.py +24 -0
  162. testerkit/analysis/measurement_facets.py +524 -0
  163. testerkit/analysis/measurements_query.py +1458 -0
  164. testerkit/analysis/metrics.py +389 -0
  165. testerkit/analysis/runs_query.py +434 -0
  166. testerkit/analysis/steps_query.py +413 -0
  167. testerkit/api/__init__.py +6 -0
  168. testerkit/api/_mime.py +71 -0
  169. testerkit/api/app.py +1171 -0
  170. testerkit/api/dialogs/__init__.py +36 -0
  171. testerkit/api/dialogs/manager.py +617 -0
  172. testerkit/api/dialogs/models.py +79 -0
  173. testerkit/api/models.py +80 -0
  174. testerkit/api/responses.py +205 -0
  175. testerkit/api/runner.py +222 -0
  176. testerkit/api/schemas.py +311 -0
  177. testerkit/benchmark/__init__.py +34 -0
  178. testerkit/benchmark/concurrency.py +248 -0
  179. testerkit/benchmark/core.py +232 -0
  180. testerkit/benchmark/runner.py +628 -0
  181. testerkit/benchmark/scenario.py +313 -0
  182. testerkit/benchmark/system.py +191 -0
  183. testerkit/benchmark/workloads.py +507 -0
  184. testerkit/catalog/__init__.py +6 -0
  185. testerkit/catalog/generic/generic_dmm.yaml +61 -0
  186. testerkit/catalog/generic/generic_eload.yaml +31 -0
  187. testerkit/catalog/generic/generic_fgen.yaml +56 -0
  188. testerkit/catalog/generic/generic_oscilloscope.yaml +45 -0
  189. testerkit/catalog/generic/generic_psu.yaml +77 -0
  190. testerkit/catalog/generic/generic_smu.yaml +73 -0
  191. testerkit/channels.py +538 -0
  192. testerkit/cli/__init__.py +32 -0
  193. testerkit/cli/_common.py +113 -0
  194. testerkit/cli/_time.py +179 -0
  195. testerkit/cli/benchmark_cmd.py +56 -0
  196. testerkit/cli/catalog_cmd.py +39 -0
  197. testerkit/cli/daemon.py +157 -0
  198. testerkit/cli/data_cmd.py +1040 -0
  199. testerkit/cli/discover_cmd.py +94 -0
  200. testerkit/cli/docs_cmd.py +100 -0
  201. testerkit/cli/instrument.py +172 -0
  202. testerkit/cli/mcp_cmd.py +37 -0
  203. testerkit/cli/metrics.py +366 -0
  204. testerkit/cli/project.py +312 -0
  205. testerkit/cli/root.py +14 -0
  206. testerkit/cli/runs.py +412 -0
  207. testerkit/cli/schema_cmd.py +87 -0
  208. testerkit/cli/serve_cmd.py +50 -0
  209. testerkit/cli/setup_cmd.py +604 -0
  210. testerkit/cli/station.py +294 -0
  211. testerkit/cli/validate.py +112 -0
  212. testerkit/client.py +463 -0
  213. testerkit/connect.py +635 -0
  214. testerkit/data/__init__.py +8 -0
  215. testerkit/data/_accumulator_pool.py +455 -0
  216. testerkit/data/_atomic.py +59 -0
  217. testerkit/data/_collection_indices.py +67 -0
  218. testerkit/data/_daemon_lifecycle.py +535 -0
  219. testerkit/data/_duckdb_daemon.py +695 -0
  220. testerkit/data/_duckdb_flight_server.py +813 -0
  221. testerkit/data/_event_filters.py +15 -0
  222. testerkit/data/_event_reader.py +101 -0
  223. testerkit/data/_flight_errors.py +199 -0
  224. testerkit/data/_flight_query.py +202 -0
  225. testerkit/data/_flight_retry.py +148 -0
  226. testerkit/data/_flight_subscribe.py +67 -0
  227. testerkit/data/_index_epoch.py +342 -0
  228. testerkit/data/_ipc_writer.py +185 -0
  229. testerkit/data/_json_safe.py +63 -0
  230. testerkit/data/_process.py +18 -0
  231. testerkit/data/_push_relay.py +124 -0
  232. testerkit/data/_runs_duckdb_daemon.py +2704 -0
  233. testerkit/data/_session_reaper.py +110 -0
  234. testerkit/data/_sql_helpers.py +30 -0
  235. testerkit/data/_store.py +24 -0
  236. testerkit/data/backends/__init__.py +7 -0
  237. testerkit/data/backends/_event_accumulator.py +957 -0
  238. testerkit/data/backends/_row_helpers.py +1172 -0
  239. testerkit/data/backends/parquet.py +1143 -0
  240. testerkit/data/backends/protocol.py +35 -0
  241. testerkit/data/channels/__init__.py +8 -0
  242. testerkit/data/channels/_flight_daemon.py +75 -0
  243. testerkit/data/channels/client.py +242 -0
  244. testerkit/data/channels/flight_manager.py +88 -0
  245. testerkit/data/channels/index.py +664 -0
  246. testerkit/data/channels/models.py +418 -0
  247. testerkit/data/channels/server.py +162 -0
  248. testerkit/data/channels/store.py +1481 -0
  249. testerkit/data/data_dir.py +61 -0
  250. testerkit/data/duckdb_manager.py +79 -0
  251. testerkit/data/event_log.py +384 -0
  252. testerkit/data/event_store.py +768 -0
  253. testerkit/data/events.py +1025 -0
  254. testerkit/data/exporters/__init__.py +6 -0
  255. testerkit/data/exporters/_helpers.py +40 -0
  256. testerkit/data/exporters/csv_exporter.py +168 -0
  257. testerkit/data/exporters/hdf5.py +234 -0
  258. testerkit/data/exporters/json_exporter.py +229 -0
  259. testerkit/data/exporters/mdf4.py +201 -0
  260. testerkit/data/exporters/stdf.py +255 -0
  261. testerkit/data/exporters/tdms.py +292 -0
  262. testerkit/data/files/__init__.py +71 -0
  263. testerkit/data/files/_backend.py +207 -0
  264. testerkit/data/files/_catalog_daemon.py +84 -0
  265. testerkit/data/files/catalog.py +218 -0
  266. testerkit/data/files/catalog_manager.py +325 -0
  267. testerkit/data/files/models.py +60 -0
  268. testerkit/data/files/serializers.py +414 -0
  269. testerkit/data/files/store.py +463 -0
  270. testerkit/data/files/streaming.py +783 -0
  271. testerkit/data/models.py +604 -0
  272. testerkit/data/ref.py +152 -0
  273. testerkit/data/retention.py +297 -0
  274. testerkit/data/run_store.py +367 -0
  275. testerkit/data/runs_duckdb_manager.py +103 -0
  276. testerkit/data/schema_dispatch.py +191 -0
  277. testerkit/data/schema_migrate.py +76 -0
  278. testerkit/data/schema_versions.py +85 -0
  279. testerkit/data/schemas.py +288 -0
  280. testerkit/data/subscribers/__init__.py +48 -0
  281. testerkit/data/subscribers/_base.py +20 -0
  282. testerkit/data/subscribers/_output_file.py +25 -0
  283. testerkit/data/subscribers/replay.py +55 -0
  284. testerkit/environment.py +84 -0
  285. testerkit/execution/__init__.py +11 -0
  286. testerkit/execution/_git.py +190 -0
  287. testerkit/execution/_state.py +740 -0
  288. testerkit/execution/accessors.py +54 -0
  289. testerkit/execution/audit.py +51 -0
  290. testerkit/execution/cascade.py +85 -0
  291. testerkit/execution/connections.py +491 -0
  292. testerkit/execution/harness.py +1931 -0
  293. testerkit/execution/instrument_events.py +42 -0
  294. testerkit/execution/limits.py +154 -0
  295. testerkit/execution/metadata.py +102 -0
  296. testerkit/execution/mocks.py +63 -0
  297. testerkit/execution/profiles.py +616 -0
  298. testerkit/execution/run_scope.py +1331 -0
  299. testerkit/execution/session_scope.py +196 -0
  300. testerkit/execution/sidecar.py +280 -0
  301. testerkit/execution/site_runner.py +705 -0
  302. testerkit/execution/sites.py +183 -0
  303. testerkit/execution/sync.py +294 -0
  304. testerkit/execution/uut_provider.py +231 -0
  305. testerkit/execution/vectors.py +205 -0
  306. testerkit/execution/verify.py +396 -0
  307. testerkit/expand.py +75 -0
  308. testerkit/files.py +235 -0
  309. testerkit/fixtures/__init__.py +6 -0
  310. testerkit/fixtures/manager.py +357 -0
  311. testerkit/grafana/__init__.py +1 -0
  312. testerkit/grafana/bootstrap.py +61 -0
  313. testerkit/grafana/cli.py +314 -0
  314. testerkit/grafana/dashboards/asset_utilization.json +92 -0
  315. testerkit/grafana/dashboards/channel_explorer.json +77 -0
  316. testerkit/grafana/dashboards/event_log.json +92 -0
  317. testerkit/grafana/dashboards/failure_pareto.json +104 -0
  318. testerkit/grafana/dashboards/measurement_distribution.json +97 -0
  319. testerkit/grafana/dashboards/measurement_trend.json +161 -0
  320. testerkit/grafana/dashboards/station_comparison.json +95 -0
  321. testerkit/grafana/dashboards/test_duration.json +104 -0
  322. testerkit/grafana/dashboards/unit_traceability.json +73 -0
  323. testerkit/grafana/dashboards/yield_overview.json +189 -0
  324. testerkit/grafana/provisioning/dashboards.yaml.j2 +12 -0
  325. testerkit/grafana/provisioning/datasources.yaml.j2 +17 -0
  326. testerkit/grafana/server.py +282 -0
  327. testerkit/init.py +724 -0
  328. testerkit/instruments/__init__.py +10 -0
  329. testerkit/instruments/base.py +82 -0
  330. testerkit/instruments/discovery/__init__.py +62 -0
  331. testerkit/instruments/discovery/_base.py +174 -0
  332. testerkit/instruments/discovery/lxi.py +152 -0
  333. testerkit/instruments/discovery/ni.py +95 -0
  334. testerkit/instruments/discovery/serial.py +73 -0
  335. testerkit/instruments/discovery/visa.py +98 -0
  336. testerkit/instruments/lifecycle.py +213 -0
  337. testerkit/instruments/loader.py +79 -0
  338. testerkit/instruments/locks.py +178 -0
  339. testerkit/instruments/mocks.py +230 -0
  340. testerkit/instruments/observer.py +270 -0
  341. testerkit/instruments/observers/__init__.py +36 -0
  342. testerkit/instruments/observers/_base.py +68 -0
  343. testerkit/instruments/observers/daqmx.py +70 -0
  344. testerkit/instruments/observers/descriptor.py +69 -0
  345. testerkit/instruments/observers/generic.py +92 -0
  346. testerkit/instruments/observers/lantz.py +31 -0
  347. testerkit/instruments/observers/modbus.py +96 -0
  348. testerkit/instruments/observers/motion.py +68 -0
  349. testerkit/instruments/observers/ni_modular.py +73 -0
  350. testerkit/instruments/observers/ophyd.py +80 -0
  351. testerkit/instruments/observers/pymeasure.py +120 -0
  352. testerkit/instruments/observers/qcodes.py +66 -0
  353. testerkit/instruments/observers/scpi.py +88 -0
  354. testerkit/instruments/observers/tektronix.py +57 -0
  355. testerkit/instruments/observers/visa.py +103 -0
  356. testerkit/instruments/pool.py +459 -0
  357. testerkit/instruments/proxy.py +59 -0
  358. testerkit/instruments/route_manager.py +329 -0
  359. testerkit/instruments/routed_proxy.py +88 -0
  360. testerkit/instruments/server.py +508 -0
  361. testerkit/instruments/switch.py +43 -0
  362. testerkit/instruments/visa.py +294 -0
  363. testerkit/matching/__init__.py +6 -0
  364. testerkit/matching/service.py +969 -0
  365. testerkit/mcp/__init__.py +6 -0
  366. testerkit/mcp/server.py +681 -0
  367. testerkit/mcp/tools.py +1868 -0
  368. testerkit/models/__init__.py +18 -0
  369. testerkit/models/capability.py +598 -0
  370. testerkit/models/catalog.py +76 -0
  371. testerkit/models/data_options.py +153 -0
  372. testerkit/models/enums.py +334 -0
  373. testerkit/models/instrument.py +152 -0
  374. testerkit/models/instrument_asset.py +25 -0
  375. testerkit/models/part.py +271 -0
  376. testerkit/models/part_manifest.py +96 -0
  377. testerkit/models/project.py +114 -0
  378. testerkit/models/station.py +140 -0
  379. testerkit/models/test_config.py +748 -0
  380. testerkit/ontology/__init__.py +39 -0
  381. testerkit/ontology/schema.py +188 -0
  382. testerkit/ontology/testerkit.yaml +1420 -0
  383. testerkit/parts/__init__.py +8 -0
  384. testerkit/parts/context.py +181 -0
  385. testerkit/parts/folder.py +251 -0
  386. testerkit/parts/loader.py +27 -0
  387. testerkit/prompts/__init__.py +17 -0
  388. testerkit/prompts/core.py +133 -0
  389. testerkit/pytest_plugin/__init__.py +1287 -0
  390. testerkit/pytest_plugin/autouse.py +369 -0
  391. testerkit/pytest_plugin/helpers.py +354 -0
  392. testerkit/pytest_plugin/hooks.py +1982 -0
  393. testerkit/pytest_plugin/markers.py +203 -0
  394. testerkit/pytest_plugin/retry.py +37 -0
  395. testerkit/pytest_plugin/sweeps.py +117 -0
  396. testerkit/queries.py +78 -0
  397. testerkit/reports/__init__.py +6 -0
  398. testerkit/reports/core.py +492 -0
  399. testerkit/reports/datasheet.py +831 -0
  400. testerkit/reports/templates/datasheet.html +383 -0
  401. testerkit/reports/templates/default.html +153 -0
  402. testerkit/sbom.py +142 -0
  403. testerkit/schema_export.py +101 -0
  404. testerkit/signals.py +63 -0
  405. testerkit/skills/README.md +58 -0
  406. testerkit/skills/templates/project-instructions.md +125 -0
  407. testerkit/skills/testerkit-analysis/SKILL.md +87 -0
  408. testerkit/skills/testerkit-capture/SKILL.md +114 -0
  409. testerkit/skills/testerkit-data/SKILL.md +138 -0
  410. testerkit/skills/testerkit-datasheets/SKILL.md +100 -0
  411. testerkit/skills/testerkit-datasheets/agents/scaffold-writer.md +115 -0
  412. testerkit/skills/testerkit-datasheets/agents/section-extractor.md +98 -0
  413. testerkit/skills/testerkit-datasheets/agents/section-reviewer.md +153 -0
  414. testerkit/skills/testerkit-datasheets/agents/section-splitter.md +89 -0
  415. testerkit/skills/testerkit-datasheets/agents/section-writer.md +226 -0
  416. testerkit/skills/testerkit-datasheets/references/catalog-pipeline.md +271 -0
  417. testerkit/skills/testerkit-datasheets/references/process-queue.md +42 -0
  418. testerkit/skills/testerkit-datasheets/references/scaffold.md +156 -0
  419. testerkit/skills/testerkit-datasheets/references/test-pipeline.md +312 -0
  420. testerkit/skills/testerkit-debug/SKILL.md +127 -0
  421. testerkit/skills/testerkit-interactive/SKILL.md +188 -0
  422. testerkit/skills/testerkit-interactive/references/live-ui-patterns.md +102 -0
  423. testerkit/skills/testerkit-mocks/SKILL.md +128 -0
  424. testerkit/skills/testerkit-parts/SKILL.md +126 -0
  425. testerkit/skills/testerkit-profiles/SKILL.md +139 -0
  426. testerkit/skills/testerkit-sites/SKILL.md +140 -0
  427. testerkit/skills/testerkit-stations/SKILL.md +146 -0
  428. testerkit/skills/testerkit-tests/SKILL.md +136 -0
  429. testerkit/store.py +1639 -0
  430. testerkit/ui/__init__.py +71 -0
  431. testerkit/ui/_asgi.py +162 -0
  432. testerkit/ui/app.py +32 -0
  433. testerkit/ui/components/__init__.py +13 -0
  434. testerkit/ui/components/artifact_viewer.py +250 -0
  435. testerkit/ui/components/channel_values.py +159 -0
  436. testerkit/ui/components/event_timeline.py +222 -0
  437. testerkit/ui/components/execution_gantt.py +210 -0
  438. testerkit/ui/components/file_streams.py +147 -0
  439. testerkit/ui/components/instrument_activity.py +146 -0
  440. testerkit/ui/components/session_table.py +118 -0
  441. testerkit/ui/pages/__init__.py +25 -0
  442. testerkit/ui/pages/channels/__init__.py +4 -0
  443. testerkit/ui/pages/channels/detail.py +932 -0
  444. testerkit/ui/pages/channels/list.py +515 -0
  445. testerkit/ui/pages/dashboard.py +236 -0
  446. testerkit/ui/pages/designer/__init__.py +5 -0
  447. testerkit/ui/pages/designer/graph.py +635 -0
  448. testerkit/ui/pages/designer/matching.py +663 -0
  449. testerkit/ui/pages/designer/page.py +593 -0
  450. testerkit/ui/pages/designer/properties.py +591 -0
  451. testerkit/ui/pages/designer/state.py +468 -0
  452. testerkit/ui/pages/docs/__init__.py +6 -0
  453. testerkit/ui/pages/docs/index.py +95 -0
  454. testerkit/ui/pages/docs/page.py +698 -0
  455. testerkit/ui/pages/events/__init__.py +3 -0
  456. testerkit/ui/pages/events/list.py +263 -0
  457. testerkit/ui/pages/explore.py +1108 -0
  458. testerkit/ui/pages/files/__init__.py +4 -0
  459. testerkit/ui/pages/files/detail.py +426 -0
  460. testerkit/ui/pages/files/list.py +447 -0
  461. testerkit/ui/pages/fixtures/__init__.py +8 -0
  462. testerkit/ui/pages/fixtures/detail.py +280 -0
  463. testerkit/ui/pages/fixtures/edit.py +370 -0
  464. testerkit/ui/pages/fixtures/list.py +180 -0
  465. testerkit/ui/pages/fixtures/new.py +195 -0
  466. testerkit/ui/pages/instruments/__init__.py +4 -0
  467. testerkit/ui/pages/instruments/detail.py +320 -0
  468. testerkit/ui/pages/instruments/edit.py +482 -0
  469. testerkit/ui/pages/instruments/list.py +249 -0
  470. testerkit/ui/pages/instruments/new.py +200 -0
  471. testerkit/ui/pages/launch.py +261 -0
  472. testerkit/ui/pages/live.py +124 -0
  473. testerkit/ui/pages/metrics_page.py +1229 -0
  474. testerkit/ui/pages/parts/__init__.py +11 -0
  475. testerkit/ui/pages/parts/detail.py +202 -0
  476. testerkit/ui/pages/parts/edit.py +310 -0
  477. testerkit/ui/pages/parts/list.py +160 -0
  478. testerkit/ui/pages/parts/new.py +170 -0
  479. testerkit/ui/pages/parts/requirements.py +153 -0
  480. testerkit/ui/pages/parts/stations.py +199 -0
  481. testerkit/ui/pages/profiles/__init__.py +4 -0
  482. testerkit/ui/pages/profiles/detail.py +113 -0
  483. testerkit/ui/pages/profiles/list.py +86 -0
  484. testerkit/ui/pages/results/__init__.py +4 -0
  485. testerkit/ui/pages/results/detail.py +631 -0
  486. testerkit/ui/pages/results/list.py +230 -0
  487. testerkit/ui/pages/stations/__init__.py +4 -0
  488. testerkit/ui/pages/stations/detail.py +256 -0
  489. testerkit/ui/pages/stations/edit.py +273 -0
  490. testerkit/ui/pages/stations/list.py +180 -0
  491. testerkit/ui/pages/stations/new.py +311 -0
  492. testerkit/ui/pages/tests/__init__.py +4 -0
  493. testerkit/ui/pages/tests/detail.py +152 -0
  494. testerkit/ui/pages/tests/list.py +194 -0
  495. testerkit/ui/pages/uuts/__init__.py +4 -0
  496. testerkit/ui/pages/uuts/list.py +97 -0
  497. testerkit/ui/shared/__init__.py +8 -0
  498. testerkit/ui/shared/components.py +1826 -0
  499. testerkit/ui/shared/dialogs.py +105 -0
  500. testerkit/ui/shared/event_binding.py +194 -0
  501. testerkit/ui/shared/layout.py +314 -0
  502. testerkit/ui/shared/services.py +1719 -0
  503. testerkit/ui/shared/timestamps.py +21 -0
  504. testerkit/ui/static/branding/favicon-16.png +0 -0
  505. testerkit/ui/static/branding/favicon-32.png +0 -0
  506. testerkit/ui/static/branding/favicon-48.png +0 -0
  507. testerkit/ui/static/branding/testerkit-icon-256.png +0 -0
  508. testerkit/ui/static/branding/testerkit-mark.svg +1 -0
  509. testerkit/ui/static/branding/testerkit-wordmark.svg +1 -0
  510. testerkit/ui/static/global.css +465 -0
  511. testerkit/utils/__init__.py +17 -0
  512. testerkit/utils/enum_meta.py +800 -0
  513. testerkit/utils/paths.py +75 -0
  514. testerkit/utils/ranges.py +257 -0
  515. testerkit/validation.py +82 -0
  516. testerkit/verbs.py +142 -0
  517. testerkit-0.4.0.dist-info/METADATA +250 -0
  518. testerkit-0.4.0.dist-info/RECORD +521 -0
  519. testerkit-0.4.0.dist-info/WHEEL +4 -0
  520. testerkit-0.4.0.dist-info/entry_points.txt +5 -0
  521. testerkit-0.4.0.dist-info/licenses/LICENSE +191 -0
@@ -0,0 +1,214 @@
1
+ # Stations
2
+
3
+ A **Station** is where you test — a physical bench with instruments. Station configs define what instruments are available and how to connect to them.
4
+
5
+ ## Station Configuration
6
+
7
+ Station configs are YAML files in `stations/`:
8
+
9
+ ```yaml
10
+ # stations/bench_1.yaml
11
+ id: bench_1
12
+ name: "Production Bench 1"
13
+ location: "Lab A"
14
+
15
+ instruments:
16
+ dmm:
17
+ type: dmm
18
+ driver: pymeasure.instruments.keysight.Keysight34461A
19
+ resource: "TCPIP::192.168.1.100::INSTR"
20
+
21
+ psu:
22
+ type: psu
23
+ driver: pymeasure.instruments.keysight.KeysightE36312A
24
+ resource: "GPIB0::5::INSTR"
25
+ ```
26
+
27
+ ## Instrument Configuration
28
+
29
+ Each instrument has:
30
+
31
+ | Field | Description |
32
+ |-------|-------------|
33
+ | `type` | Instrument type (dmm, scope, psu, eload, etc.) |
34
+ | `driver` | Import path to the driver class (PyMeasure / PyVISA / vendor) |
35
+ | `resource` | VISA address or connection string |
36
+ | `mock_config` | Canned return values used in `--mock-instruments` mode |
37
+ | `mock` | Force mock mode for this instrument |
38
+
39
+ Instruments can be shared across multiple UUT sites in a multi-UUT fixture. TesterKit connects a shared instrument once and lets each UUT's test use it one at a time, so two tests never drive the same instrument at the same moment. Sharing is detected automatically — no extra flags. See [Configuring Stations](../../how-to/configuration/configuring-stations.md#shared-instruments-multi-uut) for details.
40
+
41
+ ### Common Instrument Types
42
+
43
+ | Type | Description | Typical Capabilities |
44
+ |------|-------------|---------------------|
45
+ | `dmm` | Digital Multimeter | voltage (DC/AC), current, resistance |
46
+ | `scope` | Oscilloscope | voltage (AC), frequency, time |
47
+ | `psu` | DC Power Supply | voltage output, current output |
48
+ | `eload` | Electronic Load | current sink |
49
+ | `funcgen` | Function Generator | waveform output |
50
+
51
+ ## Mock Mode
52
+
53
+ For development without hardware, use `--mock-instruments`:
54
+
55
+ ```bash
56
+ pytest tests/ --station=stations/bench_1.yaml --mock-instruments --uut-serial=SIM001
57
+ ```
58
+
59
+ Configure mock values in the station:
60
+
61
+ ```yaml
62
+ instruments:
63
+ dmm:
64
+ type: dmm
65
+ driver: pymeasure.instruments.keysight.Keysight34461A
66
+ resource: "TCPIP::192.168.1.100::INSTR"
67
+ mock_config:
68
+ measure_dc_voltage: 3.31
69
+ measure_current: 0.1
70
+ ```
71
+
72
+ ### When to Use Mock Mode
73
+
74
+ | Approach | Use Case |
75
+ |----------|----------|
76
+ | `--mock-instruments` flag | Development, CI, station without hardware |
77
+ | Real hardware | Production, calibration |
78
+
79
+ ## Station Types and Instances
80
+
81
+ A station can be split into a reusable template and the actual bench that fills it in:
82
+
83
+ ### Station Types (Templates)
84
+
85
+ List the instrument roles and drivers a station needs:
86
+
87
+ ```yaml
88
+ # stations/types/voltage_tester.yaml
89
+ id: voltage_tester
90
+ description: "Station for voltage testing"
91
+ instruments:
92
+ dmm: {type: dmm, driver: pymeasure.instruments.keysight.Keysight34461A}
93
+ psu: {type: psu, driver: pymeasure.instruments.keysight.KeysightE36312A}
94
+ capabilities: # catalog capability ids
95
+ - keysight.34461a.dc_voltage
96
+ - keysight.e36312a.dc_source
97
+ ```
98
+
99
+ One file per station type under `stations/types/`. Every role listed under `instruments:` is required (there is no `required:` field). `capabilities:` is a list of catalog capability id strings — not inline `Capability` dicts.
100
+
101
+ ### Station Instances (Real Benches)
102
+
103
+ Concrete stations that implement a type:
104
+
105
+ ```yaml
106
+ # stations/bench_1.yaml
107
+ id: bench_1
108
+ name: "Bench 1"
109
+ station_type: voltage_tester
110
+ location: "Lab A, Bench 1"
111
+
112
+ instruments:
113
+ dmm:
114
+ type: dmm
115
+ driver: pymeasure.instruments.keysight.Keysight34461A
116
+ resource: "TCPIP::192.168.1.100::INSTR"
117
+ psu:
118
+ type: psu
119
+ driver: pymeasure.instruments.keysight.KeysightE36312A
120
+ resource: "GPIB0::5::INSTR"
121
+ ```
122
+
123
+ ### Instrument aliases per test
124
+
125
+ Station configs define the physical instrument inventory. You can override which station instrument a fixture name points to for a specific test or class — without editing the station config — using a per-test override file. See [Writing Tests](../../how-to/execution/writing-tests.md) for details.
126
+
127
+ ## Using Stations in Tests
128
+
129
+ ### Via pytest
130
+
131
+ ```bash
132
+ pytest tests/ --station=bench_1 --uut-serial=SN001
133
+ ```
134
+
135
+ ### Via fixtures
136
+
137
+ ```python
138
+ def test_voltage(dmm, measure):
139
+ """Each instrument role in the station config is available as a fixture (here: dmm)."""
140
+ measure("voltage", dmm.measure_voltage())
141
+ ```
142
+
143
+ ### Via CLI
144
+
145
+ ```bash
146
+ testerkit serve # Start UI
147
+ testerkit runs # List recent runs
148
+ testerkit show <run_id> # Show run details
149
+ ```
150
+
151
+ ## Supported Test Phases
152
+
153
+ Stations can optionally declare which test phases they support, via `supported_phases`. This lets you filter stations when selecting one for a run or profile (see [Profiles](../../how-to/execution/profiles.md)):
154
+
155
+ ```yaml
156
+ id: bench_1
157
+ name: "Production Bench 1"
158
+
159
+ supported_phases:
160
+ - validation
161
+ - production
162
+ - debug
163
+
164
+ instruments:
165
+ # ...
166
+ ```
167
+
168
+ ## Multiple Stations
169
+
170
+ Large test environments may have multiple stations:
171
+
172
+ ```
173
+ stations/
174
+ ├── _base.yaml # Station type definitions
175
+ ├── bench_1.yaml # Production bench 1
176
+ ├── bench_2.yaml # Production bench 2
177
+ ├── debug_bench.yaml # Debug station
178
+ └── ci_station.yaml # CI (simulated) station
179
+ ```
180
+
181
+ ### CI Station Example
182
+
183
+ ```yaml
184
+ # stations/ci_station.yaml
185
+ id: ci_station
186
+ name: "CI Environment"
187
+ description: "For CI/CD with --mock-instruments"
188
+
189
+ instruments:
190
+ dmm:
191
+ type: dmm
192
+ mock: true
193
+ catalog_ref: generic_dmm
194
+ mock_config:
195
+ measure_dc_voltage: 3.3
196
+ measure_current: 0.1
197
+ psu:
198
+ type: psu
199
+ mock: true
200
+ catalog_ref: generic_psu
201
+ mock_config:
202
+ measure_voltage: 5.0
203
+ ```
204
+
205
+ Run in CI:
206
+ ```bash
207
+ pytest tests/ --station=stations/ci_station.yaml --mock-instruments --uut-serial=CI-TEST
208
+ ```
209
+
210
+ ## Next Steps
211
+
212
+ - [Capabilities](capabilities.md) — Understanding what stations can do
213
+ - [Fixtures](fixtures.md) — Mapping UUT pins to instruments
214
+ - [Configuration Reference](../../reference/configuration.md) — YAML schema details
@@ -0,0 +1,199 @@
1
+ # The TesterKit data stores
2
+
3
+ TesterKit uses four complementary data stores, each optimized for a different access pattern. Together they provide a complete picture of test activity.
4
+
5
+ ## Overview
6
+
7
+ | Store | Data | Format |
8
+ |-------|------|--------|
9
+ | **EventStore** | Typed events (sessions, measurements, diagnostics) — the source of truth | Arrow files (DuckDB-queryable) |
10
+ | **ChannelStore** | Time-series instrument data (waveforms, readings) | Arrow segments |
11
+ | **FileStore** | Captured artifacts — images, video, vendor capture files (`file://`) | files + index |
12
+ | **RunStore** | Flat test results, one row per measurement | Parquet files |
13
+
14
+ ## EventStore — Source of Truth
15
+
16
+ The event log captures every significant action as a typed event. It is the canonical record of what happened.
17
+
18
+ - **Write path:** events are buffered and streamed into the DuckDB-backed event log (see [flight-streaming](flight-streaming.md) for the cross-process detail).
19
+ - **Read path:** SQL queries, or direct reads of the Arrow files.
20
+ - **Storage:** `<data_dir>/events/{date}/{session_id}-{pid}.arrow`
21
+
22
+ Each event carries only its own fields — `SessionStarted` carries the session/station/operator metadata once (and never a `run_id`); `RunStarted` carries the run/UUT context; `MeasurementRecorded` carries just the measurement. The flat run rows fill in the shared context when they're written.
23
+
24
+ ## ChannelStore — Time-Series Data
25
+
26
+ Instrument reads that produce arrays, waveforms, or high-frequency scalar streams go to the ChannelStore. The event carries a compact reference URI (`channel://scope.ch1.waveform?session=…`) that points at the data, instead of the bulk samples.
27
+
28
+ - **Write path:** samples are buffered and written to rotating Arrow segments (each closed and readable after every flush).
29
+ - **Read path:** query the channel back, with optional downsampling for plotting.
30
+ - **Storage:** `<data_dir>/channels/{date}/{channel_id}_{session_short}.arrow`
31
+
32
+ Each channel gets its own IPC file with a schema inferred from the first write. Segment rotation ensures files are always readable (closed after each flush).
33
+
34
+ ## RunStore — Analysis-Ready Results
35
+
36
+ The run store produces analysis-ready parquet files with one row per measurement, every row carrying its full context. This is what you query with DuckDB, Polars, or Spark for yield analysis and SPC.
37
+
38
+ - **Write path:** the runs daemon collects events as a run executes and writes the per-run parquet when the run ends.
39
+ - **Read path:** any Parquet reader (DuckDB, Polars, pandas).
40
+ - **Storage:** `<data_dir>/runs/{date}/{timestamp}_{run_id8}_{serial}.parquet`
41
+
42
+ The parquet files are built from the event stream — if the schema changes, they can be rebuilt from events.
43
+
44
+ ### Live streaming + crash safety
45
+
46
+ During execution, the materializer holds row state in-process and flushes to a single per-run parquet at run end. The operator UI subscribes to the in-process event stream for live updates — there is no separate JSONL journal on disk. If the process is killed before the parquet is finalized, the close-time fallback writes whatever rows reached the materializer with `run_outcome = aborted`.
47
+
48
+ ## FileStore — Captured Artifacts
49
+
50
+ Bulk artifacts — scope screenshots, camera frames, vendor capture files (`.tdms`, NPZ), any blob — go to the FileStore. Like the ChannelStore, the event carries a compact `file://` reference instead of the bytes.
51
+
52
+ - **Write path:** `observe(name, value)` with an image / `bytes` / `Path`, or `files.write` / `files.stream` directly.
53
+ - **Read path:** fetch the artifact by its `file://` reference.
54
+ - **Storage:** `<data_dir>/files/{date}/{session_id}/{filename}`
55
+
56
+ See [the three verbs](three-verbs.md) for how a test routes a value to the FileStore.
57
+
58
+ ## How They Relate
59
+
60
+ ```
61
+ Events (source of truth)
62
+ ├── RunStore — per-run parquet, written when the run ends
63
+ ├── ChannelStore — time-series; events carry a channel:// reference
64
+ └── FileStore — artifacts; events carry a file:// reference
65
+ ```
66
+
67
+ 1. All activity flows through the EventStore — the source of truth.
68
+ 2. The runs daemon collects events and writes a per-run parquet when the run ends.
69
+ 3. Array/waveform values write to the ChannelStore and artifacts to the FileStore; the event keeps a `channel://` or `file://` reference.
70
+ 4. Queries join across stores using `session_id`.
71
+
72
+ ## Storage Layout
73
+
74
+ ```
75
+ <data_dir>/
76
+ ├── events/ # EventStore
77
+ │ └── 2026-03-10/
78
+ │ └── {session_id}-{pid}.arrow
79
+ ├── channels/ # ChannelStore
80
+ │ ├── 2026-03-10/
81
+ │ │ ├── dmm.voltage_{session_short}.arrow
82
+ │ │ └── scope.ch1.waveform_{session_short}.arrow
83
+ │ └── _index.duckdb # channel descriptors + query index (single file, disposable)
84
+ ├── files/ # FileStore
85
+ │ ├── 2026-03-10/
86
+ │ │ └── {session_id}/
87
+ │ │ └── setup_photo.png
88
+ │ └── _index.duckdb # file catalog + query index (single file, disposable)
89
+ └── runs/ # RunStore (parquet)
90
+ ├── 2026-03-10/
91
+ │ └── 20260310T143022Z_a1b2c3d4_SN001.parquet
92
+ └── _index.<fingerprint>.duckdb # content-addressed query index (see below)
93
+ ```
94
+
95
+ Sessions are not a stored entity — they're derived from events at query time.
96
+
97
+ ## Where the data dir lives
98
+
99
+ `<data_dir>` defaults to a shared per-user directory so every project on the machine sees the same results pool — `testerkit runs`, `testerkit serve`, and DuckDB queries see everything.
100
+
101
+ Resolution order (first match wins):
102
+
103
+ 1. Explicit `--data-dir` argument or `data_dir=` parameter
104
+ 2. `data_dir` field in the project's `testerkit.yaml`
105
+ 3. `TESTERKIT_HOME` environment variable
106
+ 4. `~/.local/share/testerkit/data/` (platform default via `platformdirs`)
107
+
108
+ To isolate a project's results from the shared pool, add to `testerkit.yaml`:
109
+
110
+ ```yaml
111
+ name: my-project
112
+ data_dir: data # writes to ./data/ instead of the global pool
113
+ ```
114
+
115
+ ## Schema evolution — HARD contract
116
+
117
+ Parquet files are the permanent record. Each testerkit version may add columns; older files simply lack them. The parquet artifact is a **HARD contract**: changes must be additive because written files cannot be retroactively rewritten when a new version ships.
118
+
119
+ Until the 1.0 cut, the following invariants hold:
120
+
121
+ - **New columns only.** Every release may add columns. Existing column names, types, and semantics are stable across 0.x releases.
122
+ - **No removals or type changes** in 0.x. If a column would otherwise be removed or repurposed, it stays in the schema and reads as NULL for newly-written rows; the old meaning is documented as deprecated.
123
+ - **PK stability.** `(run_id, step_path, vector_index)` is the per-step identity in the materialized table; `(run_id, step_path, vector_index, measurement_name, vector_retry)` discriminates measurement rows. These tuples do not change shape in 0.x.
124
+ - **`record_type` discriminator stable.** The at-rest `'run'` / `'step'` / `'vector'` values are part of the wire format and do not change (the daemon also projects a `'measurement'` type at query time).
125
+ - **Read with `union_by_name=true`.** Consumer queries that follow the recommended `read_parquet(..., union_by_name=true)` pattern survive every additive evolution automatically.
126
+
127
+ ```sql
128
+ -- DuckDB handles mixed schemas automatically
129
+ SELECT station_id, project_name, run_outcome
130
+ FROM read_parquet('~/.local/share/testerkit/data/runs/**/*.parquet',
131
+ union_by_name=true)
132
+ ```
133
+
134
+ Schema rewrites and column removals are deferred to the 1.0 cut, when a migration story for old files lands.
135
+
136
+ ## The DuckDB query index
137
+
138
+ Every store keeps a DuckDB index alongside its data files, to speed up queries like `testerkit runs` and the web UI. The index is always a **disposable cache** — delete it and it rebuilds from the parquet or Arrow files underneath. Deleting an index file never loses data.
139
+
140
+ Two index shapes are in use, depending on the store:
141
+
142
+ **RunStore — content-addressed.** The runs index file name embeds a short fingerprint of the query it serves — the shape of the projection plus the TesterKit version that built it — as `_index.<fingerprint>.duckdb`. A query or schema change never rewrites an existing file in place: it produces a different fingerprint, and the next build writes a *new* file alongside any older ones. Multiple fingerprints can sit in the same `<data_dir>/runs/` directory at once — an older TesterKit version (or another project pinned to an older release) reading the same results keeps opening its own file, untouched by a newer index being built next to it.
143
+
144
+ **ChannelStore and FileStore — a single `_index.duckdb`.** These two stores have not adopted the content-addressed model yet; each keeps one index file that's reused (or, on a schema change, deleted and rebuilt) in place. Same disposable-cache guarantee, no coexisting versions.
145
+
146
+ ### Managing runs-index files
147
+
148
+ `testerkit data index` manages the RunStore's index files (called *epochs* — one per fingerprint):
149
+
150
+ ```cli
151
+ $ testerkit data index list
152
+ FINGERPRINT SCHEMA BUILT BY SEEN BY RUNS SIZE LAST SEEN
153
+ * a1b2c3d4e5f6 3 0.3.0 0.3.0 1,204 18.2 MB just now
154
+ f0e1d2c3b4a5 2 0.2.4 0.2.4, 0.3.0 1,204 17.9 MB 6 days ago
155
+
156
+ $ testerkit data index build # warm the current epoch
157
+ $ testerkit data index rm f0e1d2c3b4a5 # drop one epoch by its fingerprint prefix
158
+ $ testerkit data index prune # reap epochs no recent version has touched
159
+ ```
160
+
161
+ - `list` — every epoch present, with its fingerprint, schema version, the TesterKit version that built it, every version that has since read it, row count, size, and last access.
162
+ - `build` — warms the epoch matching the currently installed TesterKit version by ingesting from parquet. `--rebuild` discards it first for a clean rebuild.
163
+ - `rm <fingerprint-prefix>` — deletes one epoch. Refuses to remove the epoch actively serving queries unless `--force`.
164
+ - `prune` — removes epochs no recent TesterKit version has touched, always keeping the current epoch and (by default) the three most-recently-seen others.
165
+
166
+ None of these touch the parquet files. Every command is safe to run at any time — the worst case is that the next query pays the cost of a rebuild.
167
+
168
+ To force a full rebuild of the channels or files index, delete `_index.duckdb` under that store's directory — it rebuilds from the Arrow segments or file catalog on next access.
169
+
170
+ ## Mixed versions on one machine
171
+
172
+ When multiple projects use different TesterKit versions but share the global results directory:
173
+
174
+ | Layer | What happens | User impact |
175
+ |---|---|---|
176
+ | Parquet files | Each version writes its own schema. Newer files may have more columns. | NULL values for columns that didn't exist when the file was written. |
177
+ | Runs query index | Content-addressed: each TesterKit version reads and writes its own epoch file, keyed by its own fingerprint. Old and new epochs coexist in `<data_dir>/runs/` without contention. | `testerkit data index list` shows every epoch present; nothing to migrate by hand. Run `testerkit data index prune` to reclaim disk from epochs no version uses anymore. |
178
+ | Channels / files query index | A single shared index file per store. | Rare cross-version friction can still surface here; delete `_index.duckdb` in that store's directory to force a rebuild. |
179
+ | Web UI / CLI | Shows whatever the current process's index has. | Some fields may be empty for older runs. |
180
+
181
+ The rule: newer is always a superset. An older TesterKit version reading newer results ignores unknown columns; a newer version reading older results sees NULL for missing columns. No version corrupts or downgrades another's data.
182
+
183
+ ## See also
184
+
185
+ **Same topic, other quadrants:**
186
+
187
+ - [Reference → Parquet schema](../../reference/data/parquet-schema.md) — column-level reference for the materialized run rows
188
+ - [Reference → Query API](../../reference/data/query-api.md) — `RunsQuery` / `StepsQuery` / `MeasurementsQuery` — the read path over the run store
189
+ - [Reference → `testerkit data index`](../../reference/cli.md#cli-data-index) — full flag reference for `list` / `build` / `rm` / `prune`
190
+ - [How-to → Querying events](../../how-to/data/querying-events.md), [Querying channels](../../how-to/data/querying-channels.md) — task recipes against each store
191
+ - [How-to → Export results](../../how-to/data/export-results.md) — pulling rows out of the parquet store
192
+ - [Integration → Lakehouse import](../../integration/data/lakehouse-import.md) — pulling TesterKit parquet into your warehouse
193
+
194
+ **Sibling concepts:**
195
+
196
+ - [Event log](event-log.md) — the source-of-truth event stream
197
+ - [Event sourcing](event-sourcing.md) — why the platform is event-sourced
198
+ - [Sessions](sessions.md) — the observation window that keys events
199
+ - [Flight streaming](flight-streaming.md) — the cross-process query model
@@ -0,0 +1,185 @@
1
+ # The Event Log
2
+
3
+ The event log is TesterKit's unified record of everything that happens during testing — sessions, instrument connections, measurements, diagnostics, and more.
4
+
5
+ ## One stream, in order
6
+
7
+ Every significant action emits a typed event into a single ordered stream. That stream lets you reconstruct what happened during a test, line up instrument reads with measurements, and watch a test live as it runs.
8
+
9
+ ## Event Hierarchy
10
+
11
+ Every event carries the same common fields:
12
+
13
+ | Field | Type | Description |
14
+ |-------|------|-------------|
15
+ | `id` | UUID | Unique event identifier |
16
+ | `occurred_at` | datetime | When the event happened |
17
+ | `received_at` | datetime | When the log processed it for storage |
18
+ | `session_id` | UUID | Which session this event belongs to |
19
+ | `run_id` | UUID | Which test run (if applicable) |
20
+
21
+ Each event also carries an `event_type` string (e.g. `test.measurement`) naming which kind of event it is.
22
+
23
+ ## Event Categories
24
+
25
+ TesterKit defines events across 12 categories.
26
+
27
+ ### Session (2 events)
28
+ | Event | Type String | Description |
29
+ |-------|-------------|-------------|
30
+ | `SessionStarted` | `session.started` | Session-wide metadata: station, operator, fixture |
31
+ | `SessionEnded` | `session.ended` | Session outcome |
32
+
33
+ ### Run (3 events)
34
+ | Event | Type String | Description |
35
+ |-------|-------------|-------------|
36
+ | `RunStarted` | `run.started` | Full run context: UUT, part, operator, config snapshots |
37
+ | `RunEnded` | `run.ended` | Run outcome |
38
+ | `RunMaterialized` | `run.materialized` | Emitted after the run's Parquet file is durably written; signals that the run is ready for downstream consumers |
39
+
40
+ ### Site (2 events — multi-UUT)
41
+ | Event | Type String | Description |
42
+ |-------|-------------|-------------|
43
+ | `SiteStarted` | `site.started` | A multi-UUT site subprocess begins |
44
+ | `SiteCompleted` | `site.completed` | A multi-UUT site subprocess finishes |
45
+
46
+ ### Sync (2 events — multi-UUT)
47
+ | Event | Type String | Description |
48
+ |-------|-------------|-------------|
49
+ | `SyncArrived` | `sync.arrived` | A worker reached a synchronization barrier |
50
+ | `SyncRelease` | `sync.release` | All workers arrived; barrier released |
51
+
52
+ ### Route (2 events — signal switching)
53
+ | Event | Type String | Description |
54
+ |-------|-------------|-------------|
55
+ | `RouteClosed` | `route.closed` | Switch route closed (signal connected) |
56
+ | `RouteOpened` | `route.opened` | Switch route opened (signal disconnected) |
57
+
58
+ ### Fixture (5 events)
59
+ | Event | Type String | Description |
60
+ |-------|-------------|-------------|
61
+ | `InstrumentConnected` | `fixture.instrument_connected` | Instrument identified and connected |
62
+ | `IdentityVerified` | `fixture.identity_verified` | Expected vs actual instrument identity |
63
+ | `CalibrationWarning` | `fixture.calibration_warning` | Calibration due date approaching |
64
+ | `UutScanned` | `fixture.uut_scanned` | UUT serial barcode scanned |
65
+ | `InstrumentDisconnected` | `fixture.instrument_disconnected` | Instrument released during teardown |
66
+
67
+ ### Test (7 events)
68
+ | Event | Type String | Description |
69
+ |-------|-------------|-------------|
70
+ | `StepsDiscovered` | `test.steps_discovered` | Full list of collected test items |
71
+ | `StepStarted` | `test.step_started` | A test step begins execution |
72
+ | `MeasurementRecorded` | `test.measurement` | A single measurement with limits and outcome |
73
+ | `StepEnded` | `test.step_ended` | A test step finishes |
74
+ | `Observation` | `test.observation` | An environmental or contextual reading recorded during a step |
75
+ | `VectorStarted` | `test.vector_started` | A parametric sweep vector begins |
76
+ | `VectorEnded` | `test.vector_ended` | A parametric sweep vector finishes |
77
+
78
+ ### Instrument (2 events)
79
+ | Event | Type String | Description |
80
+ |-------|-------------|-------------|
81
+ | `InstrumentSet` | `instrument.set` | Driver set via proxy |
82
+ | `InstrumentConfigure` | `instrument.configure` | Driver configure via proxy |
83
+
84
+ ### Diagnostic (2 events)
85
+ | Event | Type String | Description |
86
+ |-------|-------------|-------------|
87
+ | `DiagnosticWarning` | `diagnostic.warning` | Non-fatal warning |
88
+ | `DiagnosticError` | `diagnostic.error` | Error condition |
89
+
90
+ ### Channel (3 events)
91
+ | Event | Type String | Description |
92
+ |-------|-------------|-------------|
93
+ | `ChannelStarted` | `channel.started` | A channel received its first sample in this session |
94
+ | `ChannelEnded` | `channel.ended` | A channel was sealed for this session |
95
+ | `ChannelCheckpoint` | `channel.checkpoint` | Liveness + progress marker from an active channel producer |
96
+
97
+ ### File (3 events)
98
+ | Event | Type String | Description |
99
+ |-------|-------------|-------------|
100
+ | `FileStarted` | `file.started` | A data stream begins |
101
+ | `FileEnded` | `file.ended` | A data stream ends |
102
+ | `FileCheckpoint` | `file.checkpoint` | Liveness + progress marker from an active file sink |
103
+
104
+ ### Dialog (2 events)
105
+ | Event | Type String | Description |
106
+ |-------|-------------|-------------|
107
+ | `DialogOpened` | `dialog.opened` | Operator dialog shown, execution paused |
108
+ | `DialogResponded` | `dialog.responded` | Operator responded to dialog |
109
+
110
+ ## Event Timeline
111
+
112
+ A typical test session emits events in this order:
113
+
114
+ ```
115
+ SessionStarted # Session-wide metadata (station, operator)
116
+ ├── RunStarted # Run context (UUT, part, config snapshots)
117
+ ├── InstrumentConnected # One per instrument role
118
+ ├── IdentityVerified # Optional identity check
119
+ ├── StepsDiscovered # Full list of collected test items
120
+ ├── StepStarted # First test step
121
+ │ ├── InstrumentSet
122
+ │ └── MeasurementRecorded
123
+ ├── StepEnded
124
+ ├── StepStarted # Next test step...
125
+ │ └── ...
126
+ ├── StepEnded
127
+ ├── RunEnded # All steps complete
128
+ ├── RunMaterialized # Parquet file durably written
129
+ ├── InstrumentDisconnected
130
+ └── SessionEnded # Cleanup complete
131
+ ```
132
+
133
+ ## How the log is written
134
+
135
+ When an event is written:
136
+
137
+ 1. **`received_at` is stamped** and the event is buffered for batched writes to an Arrow file (a fast columnar on-disk format)
138
+ 2. **On `RunEnded`** the run's Parquet file is written. The same path runs when you replay stored events with `testerkit export`.
139
+ 3. **Flush** happens every 50 events (configurable), writing a batch to the Arrow file
140
+
141
+ The set of writers (Parquet, the live UI feed) is built in; adding a new output format is a change to TesterKit itself, not a drop-in plugin.
142
+
143
+ ## Storage
144
+
145
+ Events are stored as Arrow files, date-partitioned:
146
+
147
+ ```
148
+ <data_dir>/events/
149
+ ├── 2026-03-10/
150
+ │ ├── {session_id}-{pid}.arrow
151
+ │ └── {session_id}-{pid}_0001.arrow # rotation for large sessions
152
+ └── 2026-03-11/
153
+ └── ...
154
+ ```
155
+
156
+ Each Arrow file contains index columns (`id`, `event_type`, `occurred_at`, `received_at`, `session_id`, `run_id`) plus a `json` column with the full serialized event for lossless replay.
157
+
158
+ ## Dual-Write Pattern
159
+
160
+ Events are also loaded into an in-memory database as they're written, so you can query them with SQL right away:
161
+
162
+ 1. **Arrow file** — crash-safe append-only storage
163
+ 2. **In-memory DuckDB** — immediate SQL queryability
164
+
165
+ Each batch is loaded into an in-memory SQL database as it's written, so a query sees an event the instant after it's written.
166
+
167
+ ## What stays stable across releases
168
+
169
+ Once an event type and its fields exist, they don't change or disappear within 0.x. Only new event types and new optional fields are added. A query or report you write today keeps working as the platform evolves.
170
+
171
+ ## See also
172
+
173
+ **Same topic, other quadrants:**
174
+
175
+ - [Reference → Event types](../../reference/data/event-types.md) — generated field reference for every event class
176
+ - [Reference → Parquet schema](../../reference/data/parquet-schema.md) — the materialized projection of these events
177
+ - [How-to → Querying events](../../how-to/data/querying-events.md) — MCP / HTTP / Python recipes for reading the event log
178
+ - [Operator UI → Events](../../reference/operator-ui/events.md) — the browser view of the event log
179
+ - [Tutorial → Step 10: Live monitoring](../../tutorial/10-live-monitoring.md) — first hands-on with events as they happen
180
+
181
+ **Sibling concepts:**
182
+
183
+ - [Event sourcing](event-sourcing.md) — why the platform is event-sourced rather than mutation-based
184
+ - [Data stores](data-stores.md) — how EventStore fits with ChannelStore, FileStore, and RunStore
185
+ - [Sessions](sessions.md) — the observation window the event log keys events by