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
testerkit/__init__.py ADDED
@@ -0,0 +1,68 @@
1
+ """TesterKit - Hardware test platform for the AI-assisted era.
2
+
3
+ User-facing surface. Everything in this module is part of the public
4
+ API contract that test writers, interactive users, custom UI builders,
5
+ and data-mover scripts should reach for.
6
+
7
+ Three import shapes — pick the shape that matches your use:
8
+
9
+ **Top-level names** — frequently-typed surfaces, available directly::
10
+
11
+ from testerkit import (
12
+ connect, verify, observe, stream,
13
+ Limit, Waveform, XYData, Outcome,
14
+ arange, linspace, logspace, geomspace, repeat,
15
+ Mock, TesterKitClient,
16
+ )
17
+
18
+ **Grouped submodules** — related surfaces under a single namespace::
19
+
20
+ import testerkit.channels # channels.write, channels.stream
21
+ import testerkit.files # files.write, files.stream
22
+ import testerkit.queries # queries.RunsQuery, queries.MeasurementsQuery, ...
23
+
24
+ **Deep paths** — internals for TesterKit contributors only. If you find
25
+ yourself reaching for one of these in a test, example, doc, or
26
+ external script, file an issue — the user-facing surface is missing
27
+ a re-export.
28
+ """
29
+
30
+ from importlib.metadata import PackageNotFoundError
31
+ from importlib.metadata import version as _pkg_version
32
+
33
+ from testerkit.client import TesterKitClient
34
+ from testerkit.connect import connect
35
+ from testerkit.data.models import Outcome, Waveform, XYData
36
+ from testerkit.expand import arange, geomspace, linspace, logspace, repeat
37
+ from testerkit.instruments.mocks import Mock
38
+ from testerkit.models.test_config import Limit
39
+ from testerkit.verbs import measure, observe, stream, verify
40
+
41
+ try:
42
+ # Single source of truth — read from the installed wheel's metadata
43
+ # so __version__ tracks pyproject.toml automatically. Editable
44
+ # installs (`uv pip install -e .`) populate metadata too.
45
+ __version__ = _pkg_version("testerkit")
46
+ except PackageNotFoundError:
47
+ # Imported from a source tree without `pip install`; safe fallback.
48
+ __version__ = "0.0.0+unknown"
49
+
50
+ __all__ = [
51
+ "__version__",
52
+ "Limit",
53
+ "TesterKitClient",
54
+ "Mock",
55
+ "Outcome",
56
+ "Waveform",
57
+ "XYData",
58
+ "arange",
59
+ "connect",
60
+ "geomspace",
61
+ "linspace",
62
+ "logspace",
63
+ "measure",
64
+ "observe",
65
+ "repeat",
66
+ "stream",
67
+ "verify",
68
+ ]
@@ -0,0 +1,447 @@
1
+ # Capabilities
2
+
3
+ **Capabilities** describe what instruments can do and what parts need. The capability system enables automatic matching between parts and stations using a signal-parameter model inspired by ATML (Automatic Test Markup Language) and IEEE 1641.
4
+
5
+ ## The problem
6
+
7
+ A Keysight 34461A datasheet says it can measure "DC Voltage: 100 mV to 1000 V, 0.0035% + 0.0006% accuracy, 6.5-digit resolution." A part spec says "3.3 V output, ±5% tolerance." How do we connect these two worlds in a machine-readable way so the system can automatically determine whether a given instrument can test a given part?
8
+
9
+ We need a shared language that works for both sides. That language is `Capability`.
10
+
11
+ ## What Is a Capability?
12
+
13
+ A capability has three core dimensions:
14
+
15
+ | Dimension | Examples | Description |
16
+ |-----------|---------|-------------|
17
+ | `function` | dc_voltage, ac_voltage, resistance, waveform, s_parameters, phase_noise, ... | Named measurement function |
18
+ | `direction` | input, output, bidir, transform | Does it measure, source, or transform? |
19
+ | `signals/conditions/controls/attributes` | voltage: {range: 0-1000V}, bandwidth: {value: 50MHz} | Named signal parameters organized by semantic role |
20
+
21
+ ### Model Hierarchy
22
+
23
+ ```
24
+ Capability (base)
25
+ ├── InstrumentCapability — adds channels, readback
26
+ └── PartCharacteristic — adds pin / pins / net / signal_group, datasheet_ref
27
+ ```
28
+
29
+ Every part characteristic must name at least one physical interface (`pin`, `pins`, `net`, or `signal_group`) — TesterKit won't load one without it.
30
+
31
+ Both share the same `function + direction + signals/conditions/controls/attributes` core. Direction always describes the hardware it's on: "input" means "this device receives/sinks signal."
32
+
33
+ ### Example: DMM Capabilities
34
+
35
+ ```yaml
36
+ # catalog/keysight_34461a.yaml or instruments/dmm.yaml
37
+ capabilities:
38
+ - function: dc_voltage
39
+ direction: input # Instrument measures (receives signal)
40
+ signals:
41
+ voltage:
42
+ range: {min: 0.0001, max: 1000, unit: V}
43
+ accuracy: {pct_reading: 0.0035, pct_range: 0.0006}
44
+ resolution: {digits: 6.5}
45
+
46
+ - function: dc_current
47
+ direction: input
48
+ signals:
49
+ current:
50
+ range: {min: 0.000001, max: 10, unit: A}
51
+
52
+ - function: resistance
53
+ direction: input
54
+ signals:
55
+ resistance:
56
+ range: {min: 0.01, max: 100000000, unit: Ohm}
57
+ ```
58
+
59
+ ### Example: Power Supply Capabilities
60
+
61
+ ```yaml
62
+ # catalog/keysight_e36312a.yaml or instruments/psu.yaml
63
+
64
+ # Top-level structured channels describe physical topology
65
+ channels:
66
+ "1": {terminals: [hi, lo], connector: binding_post, ground: floating}
67
+ "2": {terminals: [hi, lo], connector: binding_post, ground: floating}
68
+
69
+ capabilities:
70
+ - function: dc_voltage
71
+ direction: output # Instrument sources (provides signal)
72
+ signals:
73
+ voltage:
74
+ range: {min: 0, max: 30, unit: V}
75
+ current:
76
+ range: {min: 0, max: 5, unit: A}
77
+ channels: ["1", "2"] # References to top-level channel keys
78
+
79
+ - function: dc_voltage
80
+ direction: input # Built-in readback meter
81
+ readback: true # Marks a built-in readback meter
82
+ signals:
83
+ voltage:
84
+ range: {min: 0, max: 30, unit: V}
85
+ channels: ["1", "2"]
86
+ ```
87
+
88
+ ## Direction Pairing
89
+
90
+ The key insight is that **directions pair** between parts and instruments:
91
+
92
+ ```
93
+ Part Characteristic Required Instrument Capability
94
+ ───────────────────── ────────────────────────────────
95
+ output_voltage (OUTPUT) → dc_voltage (INPUT) — need to measure
96
+ input_voltage (INPUT) → dc_voltage (OUTPUT) — need to source
97
+ ```
98
+
99
+ ### Why This Works
100
+
101
+ When a part **outputs** voltage, the instrument needs to **input** (measure) that voltage.
102
+
103
+ When a part **inputs** power, the instrument needs to **output** (source) that power.
104
+
105
+ ```
106
+ Part (UUT) Instrument
107
+ ──────────── ──────────
108
+
109
+ output_voltage ────signal───► DMM (measures dc_voltage)
110
+ (OUTPUT) (INPUT)
111
+
112
+ ◄───power──── PSU (sources dc_voltage)
113
+ input_voltage (OUTPUT)
114
+ (INPUT)
115
+ ```
116
+
117
+ You always write `direction` from the point of view of the hardware it sits on: `input` means that device receives signal, `output` means it sources signal — the same on a part or an instrument. The matcher pairs an OUTPUT on one side with an INPUT on the other.
118
+
119
+ ## Capability Matching
120
+
121
+ The matcher determines whether a station can test a part by checking in tiers — you can stop at any tier, from function-only down through accuracy and resolution:
122
+
123
+ 1. **Function match** — instrument has same `MeasurementFunction` as requirement
124
+ 2. **Direction match** — directions pair correctly (part OUTPUT ↔ instrument INPUT, and vice versa). A `bidir` instrument satisfies any part direction; a `bidir` part needs a `bidir` instrument.
125
+ 3. **Parameter range containment** — instrument's parameter ranges contain required values
126
+ 4. **Accuracy** — instrument accuracy must be better than required (condition-aware via [`SpecBand`](../../reference/data/models.md), the value-plus-condition record)
127
+ 5. **Resolution** — instrument resolution must meet or exceed required
128
+
129
+ ```python
130
+ from testerkit.matching.service import find_compatible_stations
131
+ from testerkit.store import get_part
132
+
133
+ # Load by id (`get_part` looks up `parts/<id>.yaml` from the project root).
134
+ # Use `load_part(Path(...))` when you have an explicit path on disk.
135
+ part = get_part("power_board")
136
+ matches = find_compatible_stations(part) # takes the loaded Part object
137
+
138
+ for match in matches:
139
+ print(f"{match.station_id}: {'Compatible' if match.compatible else 'Missing capabilities'}")
140
+ ```
141
+
142
+ ### Matching Algorithm
143
+
144
+ ```python
145
+ # Part characteristic
146
+ char = part.characteristics["output_voltage"]
147
+ # function: dc_voltage, direction: OUTPUT
148
+
149
+ # Matching wraps characteristics into CapabilityRequirement
150
+ # Direction stays as-is (OUTPUT) — the matcher pairs the part's OUTPUT with the DMM's INPUT
151
+
152
+ # Station instrument provides:
153
+ # function: dc_voltage, direction: INPUT, signals: {voltage: {range: 0-1000V}}
154
+ # → Function match ✓, Direction pair (OUTPUT↔INPUT) ✓, Range contains 3.3V ✓
155
+ # → MATCH!
156
+ ```
157
+
158
+ ## MeasurementFunction
159
+
160
+ The `MeasurementFunction` enum provides fine-grained signal identification, aligned with IVI (Interchangeable Virtual Instrument Foundation) instrument class specifications:
161
+
162
+ | Function | Description | Typical Instrument |
163
+ |----------|-------------|--------------------|
164
+ | **Basic Electrical** | | |
165
+ | `dc_voltage` | DC voltage measurement/sourcing | DMM, PSU |
166
+ | `ac_voltage` | AC voltage measurement | DMM |
167
+ | `dc_current` | DC current measurement/sourcing | DMM, PSU, SMU |
168
+ | `ac_current` | AC current measurement | DMM, clamp meter |
169
+ | `dc_power` | DC power measurement/calculation | SMU, derived |
170
+ | `ac_power` | AC power measurement | Power meter |
171
+ | `resistance` | 2-wire resistance | DMM |
172
+ | `resistance_4w` | 4-wire resistance | DMM |
173
+ | `capacitance` | Capacitance measurement | LCR meter, DMM |
174
+ | `inductance` | Inductance measurement | LCR meter |
175
+ | `impedance` | Impedance measurement | Impedance analyzer |
176
+ | **RLC Parameters** | | |
177
+ | `quality_factor` | Quality factor (Q) | LCR meter |
178
+ | `dissipation_factor` | Dissipation factor (D) | LCR meter |
179
+ | **Time/Frequency** | | |
180
+ | `frequency` | Frequency measurement | DMM, counter |
181
+ | `period` | Period measurement | Counter |
182
+ | `time_interval` | Time interval between events | Counter |
183
+ | `pulse_width` | Pulse width measurement | Counter, scope |
184
+ | `duty_cycle` | Duty cycle measurement | Counter, scope |
185
+ | `phase` | Phase measurement | Counter, scope |
186
+ | **Waveform** | | |
187
+ | `waveform` | Time-domain waveform capture/generation | Oscilloscope, FGen |
188
+ | **Edge Timing** | | |
189
+ | `rise_time` | Rise time measurement | Oscilloscope |
190
+ | `fall_time` | Fall time measurement | Oscilloscope |
191
+ | **RF Measurements** | | |
192
+ | `rf_power` | RF power measurement | Power meter |
193
+ | `rf_cw` | CW signal generation | RF signal generator |
194
+ | `s_parameters` | S-parameter measurement | VNA |
195
+ | `spectrum` | Frequency-domain analysis | Spectrum analyzer |
196
+ | `phase_noise` | Phase noise measurement | Signal/spectrum analyzer |
197
+ | `noise_figure` | Noise figure measurement | NF analyzer |
198
+ | `harmonics` | Harmonic distortion | Spectrum analyzer |
199
+ | **Digital/Logic** | | |
200
+ | `digital_pattern` | Digital pattern generation/capture | Logic analyzer, pattern gen |
201
+ | `digital_io` | Digital I/O, GPIO | Digital I/O card |
202
+ | `serial_data` | Serial protocol decode | Logic analyzer |
203
+ | **Signal Integrity** | | |
204
+ | `jitter` | Jitter measurement | Oscilloscope, TIA |
205
+ | `eye_diagram` | Eye diagram analysis | Oscilloscope |
206
+ | `power_quality` | Power quality analysis | Power analyzer |
207
+ | **Miscellaneous** | | |
208
+ | `temperature` | Temperature measurement | DMM (RTD/TC), chamber |
209
+ | `diode` | Diode test | DMM |
210
+ | `continuity` | Continuity test | DMM |
211
+ | `optical_power` | Optical power measurement | Optical power meter |
212
+ | `wavelength` | Wavelength measurement | Optical spectrum analyzer |
213
+ | `magnetic_field` | Magnetic field measurement | Gaussmeter |
214
+ | `position` | Position/displacement | Encoder, stage controller |
215
+
216
+ The `transform` direction is used for signal-path components (amplifiers, filters, mixers) that modify signals rather than measuring or sourcing them.
217
+
218
+ Each function names one measurement precisely — a DMM measuring `dc_voltage` and a scope capturing `waveform` are separate functions.
219
+
220
+ ### Waveform Shapes
221
+
222
+ For function generators and waveform sources, the `MeasurementFunction.WAVEFORM` is used with a `WaveformShape` parameter to specify supported shapes:
223
+
224
+ ```yaml
225
+ # Function generator waveform shapes
226
+ - sine
227
+ - square
228
+ - triangle
229
+ - ramp
230
+ - pulse
231
+ - arbitrary
232
+ - noise
233
+ - dc
234
+ ```
235
+
236
+ Per IEEE 1641 (the test-vocabulary standard underlying ATML), waveform shapes are **characteristics of the signal**, not distinct signal types. Function generators should have a single `function: waveform` capability rather than separate capabilities for each shape.
237
+
238
+ ## Typed Collections
239
+
240
+ Each capability organizes parameters into four semantic categories:
241
+
242
+ | Collection | Purpose | Examples |
243
+ |-----------|---------|----------|
244
+ | `signals` | What's being measured/sourced | voltage, current, resistance — has range, accuracy, resolution |
245
+ | `conditions` | Operating conditions affecting accuracy | frequency, temperature, load — range only, used for SpecBand matching |
246
+ | `controls` | User knobs that can be configured | attenuation, filter_type, range_select — range or options |
247
+ | `attributes` | Fixed facts about the capability | bandwidth, max_frequency — fixed value, no comparison needed |
248
+
249
+ ### Why four separate collections?
250
+
251
+ Each collection has a well-defined purpose:
252
+
253
+ - **Signals** participate in matching and have accuracy/range specs.
254
+ - **Conditions** appear in SpecBand `when:` constraints but don't participate directly in matching logic.
255
+ - **Controls** are user-configurable but don't affect the fundamental capability.
256
+ - **Attributes** are hardware facts that may participate in matching (e.g. scope bandwidth must exceed signal frequency), but are never measured — they're just limits.
257
+
258
+ A dimension name can't appear in more than one of `signals` / `conditions` / `controls` — TesterKit won't load a capability with that overlap. A name in `attributes` doesn't collide with the others.
259
+
260
+ ### Condition-Dependent Specs (SpecBand)
261
+
262
+ Instrument accuracy often varies with operating conditions. A DMM's AC voltage accuracy depends on the input frequency. A `SpecBand` captures this:
263
+
264
+ ```yaml
265
+ signals:
266
+ voltage:
267
+ range: {min: 0.1, max: 750, unit: V}
268
+ accuracy: {pct_reading: 0.07, pct_range: 0.02} # default
269
+ bands:
270
+ - when:
271
+ frequency: {min: 3, max: 5, unit: Hz}
272
+ accuracy: {pct_reading: 0.35, pct_range: 0.03}
273
+ - when:
274
+ frequency: {min: 5, max: 300, unit: Hz}
275
+ accuracy: {pct_reading: 0.07, pct_range: 0.02}
276
+ - when:
277
+ frequency: {min: 300, max: 300000, unit: Hz}
278
+ accuracy: {pct_reading: 0.14, pct_range: 0.05}
279
+ conditions:
280
+ frequency:
281
+ range: {min: 3, max: 300000, unit: Hz}
282
+ ```
283
+
284
+ The `when:` keys reference the flat union of `signals`, `conditions`, and `controls` on the parent capability — any sibling dimension name is valid. Multiple keys are ANDed (all must match). When no band matches, the top-level accuracy/resolution applies as a default.
285
+
286
+ ### Canonical condition keys
287
+
288
+ `ConditionKey` is a shared vocabulary for the `conditions` dict (27 canonical keys derived from auditing 150+ instrument datasheets). It's not enforced at the model level — you can use any string — but matching across catalog entries is more reliable when authors converge on these names:
289
+
290
+ | Category | Keys |
291
+ |---|---|
292
+ | Universal | `frequency`, `temperature`, `humidity`, `calibration_interval` |
293
+ | Measurement config | `nplc`, `auto_zero`, `coupling`, `impedance`, `sense_mode`, `sample_rate`, `bandwidth`, `filter`, `gate_time`, `acquisition_mode`, `time_constant` |
294
+ | Signal | `signal_level`, `crest_factor` |
295
+ | Source / load | `load`, `input_voltage`, `voltage`, `current`, `duty_cycle`, `slew_rate`, `settling_time` |
296
+ | Sensor | `sensor`, `wavelength` |
297
+ | RF | `offset` |
298
+
299
+ ## Channel Specification
300
+
301
+ Channels describe the physical topology of each instrument channel:
302
+
303
+ ```yaml
304
+ # Structured channel topology (on catalog/instrument library):
305
+ channels:
306
+ "1":
307
+ label: "6V/5A Output"
308
+ terminals: [hi, lo, sense_hi, sense_lo] # Physical terminals
309
+ connector: binding_post # Connector type
310
+ ground: floating # Isolated from other channels
311
+ "2":
312
+ terminals: [hi, lo]
313
+ connector: binding_post
314
+ ground: floating
315
+
316
+ # On capabilities, channels is a plain list of keys:
317
+ capabilities:
318
+ - function: dc_voltage
319
+ direction: output
320
+ channels: ["1", "2"] # Which channels support this capability
321
+ ```
322
+
323
+ ## 3-Tier Instrument Catalog
324
+
325
+ Capability data lives at three levels:
326
+
327
+ ```
328
+ catalog/keysight_34461a.yaml ← Universal: "what can this MODEL do"
329
+ instruments/dmm_bench_001.yaml ← Unit-specific: serial, calibration, catalog_ref
330
+ stations/bench_01.yaml ← Project-local: role, driver, resource, catalog_ref
331
+ ```
332
+
333
+ When a station instrument has `catalog_ref: keysight_34461a`, the matching engine resolves capabilities from the catalog entry — providing detailed range/accuracy data without requiring each station config to repeat it.
334
+
335
+ ## Using the Matcher
336
+
337
+ ### Python API
338
+
339
+ ```python
340
+ from testerkit.matching.service import find_compatible_stations, check_station_compatibility
341
+
342
+ # Find all compatible stations (takes the loaded Part object)
343
+ matches = find_compatible_stations(part)
344
+
345
+ # Check specific station — takes id strings, returns dict | None
346
+ result = check_station_compatibility(part_id, station_id)
347
+ if result and not result["compatible"]:
348
+ for cap in result["missing"]:
349
+ print(f"Missing: {cap['direction']} {cap['function']}")
350
+ ```
351
+
352
+ `find_compatible_stations(part)` takes a loaded `Part` object and returns a `list[StationMatch]`. `check_station_compatibility(part_id, station_id)` takes id strings and returns a `dict | None`; its `missing` value is a list of dicts shaped `{characteristic, function, direction}`.
353
+
354
+ ### HTTP API
355
+
356
+ ```bash
357
+ # Find all compatible stations
358
+ curl "http://localhost:8000/api/match?part_id=power_board"
359
+
360
+ # Check specific station
361
+ curl "http://localhost:8000/api/match?part_id=power_board&station_id=bench_1"
362
+ ```
363
+
364
+ ## Where this model comes from
365
+
366
+ The capability model draws on established test standards: signal-oriented thinking from IEEE 1641 (capabilities describe signals, not instruments; waveform shapes are parameters, not types), the comparator types and direction-pairing concept from ATML / IEEE 1671, and function names from the IVI Foundation instrument classes (DMM, Scope, FGen, DCPwr, RFSigGen). Parts and instruments use the same model, so matching pairs them directly — no translation step.
367
+
368
+ ## Custom Instruments
369
+
370
+ When adding custom instruments, define their capabilities using the function-parameter model:
371
+
372
+ ```yaml
373
+ # instruments/temp_logger.yaml
374
+ instrument:
375
+ type: temp_logger
376
+ name: Custom Temperature Logger
377
+
378
+ channels:
379
+ "T1": {terminals: [signal], connector: terminal_block, ground: shared}
380
+ "T2": {terminals: [signal], connector: terminal_block, ground: shared}
381
+ "T3": {terminals: [signal], connector: terminal_block, ground: shared}
382
+ "T4": {terminals: [signal], connector: terminal_block, ground: shared}
383
+ "T5": {terminals: [signal], connector: terminal_block, ground: shared}
384
+ "T6": {terminals: [signal], connector: terminal_block, ground: shared}
385
+ "T7": {terminals: [signal], connector: terminal_block, ground: shared}
386
+ "T8": {terminals: [signal], connector: terminal_block, ground: shared}
387
+
388
+ capabilities:
389
+ - function: temperature
390
+ direction: input
391
+ signals:
392
+ temperature:
393
+ range: {min: -200, max: 850, unit: "°C"}
394
+ channels: ["T1", "T2", "T3", "T4", "T5", "T6", "T7", "T8"]
395
+ ```
396
+
397
+ ## The full picture
398
+
399
+ ```
400
+ Instrument catalog — what an instrument CAN do
401
+ ──────────────────────────────────────────────
402
+ catalog_entry:
403
+ id: keysight_34461a
404
+ type: dmm
405
+ channels:
406
+ "1": {terminals: [hi, lo], connector: binding_post, ground: shared}
407
+
408
+ capabilities:
409
+ - function: dc_voltage ─┐
410
+ direction: input │ Capability
411
+ signals: │ (shared base class)
412
+ voltage: │
413
+ range: {min: 0, max: 1000, unit: V}
414
+ accuracy: {pct_reading: 0.0035}
415
+ resolution: {digits: 6.5}
416
+
417
+ Part spec — what the UUT NEEDS tested
418
+ ────────────────────────────────────────
419
+ id: power_board_v1
420
+ pins:
421
+ VOUT: {name: "J1.3", net: "VOUT_3V3", role: signal}
422
+
423
+ characteristics:
424
+ rail_3v3:
425
+ function: dc_voltage ─┐
426
+ direction: output │ PartCharacteristic
427
+ pin: VOUT │ (extends Capability)
428
+ signals: │
429
+ voltage: │
430
+ value: 3.3 │
431
+ unit: V │
432
+ bands: │
433
+ - when: │
434
+ load: {min: 0, max: 1} │
435
+ accuracy: {pct_reading: 5.0}
436
+
437
+ Matching: dc_voltage OUTPUT ↔ dc_voltage INPUT
438
+ 3.3 V within 0–1000 V range
439
+ 0.0035% << 5% (instrument is more accurate)
440
+ MATCH
441
+ ```
442
+
443
+ ## Next Steps
444
+
445
+ - [Fixtures](fixtures.md) — Mapping UUT pins to instruments
446
+ - [Architecture](../overview/architecture.md) — System data flow
447
+ - [Custom drivers](../../how-to/configuration/custom-drivers.md) — Creating custom drivers