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.
- testerkit/__init__.py +68 -0
- testerkit/_docs/_assets/operator-ui/channels/table.png +0 -0
- testerkit/_docs/_assets/operator-ui/dashboard/runs.png +0 -0
- testerkit/_docs/_assets/operator-ui/dashboard/stations.png +0 -0
- testerkit/_docs/_assets/operator-ui/events/filters.png +0 -0
- testerkit/_docs/_assets/operator-ui/events/table.png +0 -0
- testerkit/_docs/_assets/operator-ui/explore/chart.png +0 -0
- testerkit/_docs/_assets/operator-ui/explore/filters.png +0 -0
- testerkit/_docs/_assets/operator-ui/explore/plot-controls.png +0 -0
- testerkit/_docs/_assets/operator-ui/files/table.png +0 -0
- testerkit/_docs/_assets/operator-ui/launch/form.png +0 -0
- testerkit/_docs/_assets/operator-ui/metrics/filters.png +0 -0
- testerkit/_docs/_assets/operator-ui/metrics/pareto.png +0 -0
- testerkit/_docs/_assets/operator-ui/metrics/ppk.png +0 -0
- testerkit/_docs/_assets/operator-ui/metrics/yield.png +0 -0
- testerkit/_docs/_assets/operator-ui/results/detail-header.png +0 -0
- testerkit/_docs/_assets/operator-ui/results/detail-measurements.png +0 -0
- testerkit/_docs/_assets/operator-ui/results/detail-overview.png +0 -0
- testerkit/_docs/_assets/operator-ui/results/detail-steps.png +0 -0
- testerkit/_docs/_assets/operator-ui/results/stats.png +0 -0
- testerkit/_docs/_assets/operator-ui/results/table.png +0 -0
- testerkit/_docs/_assets/operator-ui/tour/designer.png +0 -0
- testerkit/_docs/_assets/operator-ui/tour/docs.png +0 -0
- testerkit/_docs/_assets/operator-ui/tour/fixtures.png +0 -0
- testerkit/_docs/_assets/operator-ui/tour/instruments.png +0 -0
- testerkit/_docs/_assets/operator-ui/tour/parts.png +0 -0
- testerkit/_docs/_assets/operator-ui/tour/profiles.png +0 -0
- testerkit/_docs/_assets/operator-ui/tour/stations.png +0 -0
- testerkit/_docs/_assets/operator-ui/tour/tests.png +0 -0
- testerkit/_docs/_assets/operator-ui/tour/uuts.png +0 -0
- testerkit/_docs/concepts/configuration/capabilities.md +447 -0
- testerkit/_docs/concepts/configuration/fixtures.md +335 -0
- testerkit/_docs/concepts/configuration/index.md +14 -0
- testerkit/_docs/concepts/configuration/parts.md +276 -0
- testerkit/_docs/concepts/configuration/stations.md +214 -0
- testerkit/_docs/concepts/data/data-stores.md +199 -0
- testerkit/_docs/concepts/data/event-log.md +185 -0
- testerkit/_docs/concepts/data/event-sourcing.md +76 -0
- testerkit/_docs/concepts/data/flight-streaming.md +58 -0
- testerkit/_docs/concepts/data/index.md +17 -0
- testerkit/_docs/concepts/data/sessions.md +78 -0
- testerkit/_docs/concepts/data/three-verbs.md +260 -0
- testerkit/_docs/concepts/execution/index.md +13 -0
- testerkit/_docs/concepts/execution/outcomes.md +199 -0
- testerkit/_docs/concepts/execution/step-hierarchy.md +170 -0
- testerkit/_docs/concepts/execution/step-manifest.md +115 -0
- testerkit/_docs/concepts/index.md +40 -0
- testerkit/_docs/concepts/overview/ai-integration.md +57 -0
- testerkit/_docs/concepts/overview/architecture.md +357 -0
- testerkit/_docs/concepts/overview/index.md +14 -0
- testerkit/_docs/concepts/overview/platform-vs-framework.md +147 -0
- testerkit/_docs/concepts/overview/pytest.md +53 -0
- testerkit/_docs/concepts/overview/tiers.md +46 -0
- testerkit/_docs/how-to/catalog/datasheet-to-test.md +169 -0
- testerkit/_docs/how-to/catalog/index.md +11 -0
- testerkit/_docs/how-to/configuration/configuring-stations.md +364 -0
- testerkit/_docs/how-to/configuration/custom-drivers.md +484 -0
- testerkit/_docs/how-to/configuration/index.md +13 -0
- testerkit/_docs/how-to/configuration/mock-mode.md +342 -0
- testerkit/_docs/how-to/data/benchmarking.md +120 -0
- testerkit/_docs/how-to/data/capture-an-artifact.md +84 -0
- testerkit/_docs/how-to/data/capture-waveform.md +110 -0
- testerkit/_docs/how-to/data/choosing-a-channel-verb.md +108 -0
- testerkit/_docs/how-to/data/compare-runs.md +120 -0
- testerkit/_docs/how-to/data/export-results.md +104 -0
- testerkit/_docs/how-to/data/find-flaky-tests.md +108 -0
- testerkit/_docs/how-to/data/grafana-dashboards.md +148 -0
- testerkit/_docs/how-to/data/index.md +23 -0
- testerkit/_docs/how-to/data/mcp-debug-failures.md +125 -0
- testerkit/_docs/how-to/data/mcp-query-runs.md +120 -0
- testerkit/_docs/how-to/data/querying-channels.md +117 -0
- testerkit/_docs/how-to/data/querying-events.md +85 -0
- testerkit/_docs/how-to/data/stream-live-channel.md +134 -0
- testerkit/_docs/how-to/execution/custom-operator-ui.md +136 -0
- testerkit/_docs/how-to/execution/index.md +21 -0
- testerkit/_docs/how-to/execution/limits.md +193 -0
- testerkit/_docs/how-to/execution/managing-sessions.md +100 -0
- testerkit/_docs/how-to/execution/multi-uut-testing.md +211 -0
- testerkit/_docs/how-to/execution/operator-prompts.md +151 -0
- testerkit/_docs/how-to/execution/profiles.md +304 -0
- testerkit/_docs/how-to/execution/spec-driven-testing.md +169 -0
- testerkit/_docs/how-to/execution/test-context.md +212 -0
- testerkit/_docs/how-to/execution/traceability.md +252 -0
- testerkit/_docs/how-to/execution/vector-expansion.md +394 -0
- testerkit/_docs/how-to/execution/writing-tests.md +363 -0
- testerkit/_docs/how-to/index.md +59 -0
- testerkit/_docs/how-to/overview/index.md +11 -0
- testerkit/_docs/how-to/overview/mcp-integration.md +168 -0
- testerkit/_docs/how-to/overview/operator-ui-tour.md +234 -0
- testerkit/_docs/integration/data/grafana.md +162 -0
- testerkit/_docs/integration/data/index.md +15 -0
- testerkit/_docs/integration/data/lakehouse-import.md +226 -0
- testerkit/_docs/integration/data/logging.md +131 -0
- testerkit/_docs/integration/data/results-api.md +51 -0
- testerkit/_docs/integration/index.md +20 -0
- testerkit/_docs/integration/runtime/harness.md +281 -0
- testerkit/_docs/integration/runtime/index.md +13 -0
- testerkit/_docs/integration/runtime/instruments.md +284 -0
- testerkit/_docs/integration/runtime/pytest-existing.md +315 -0
- testerkit/_docs/reference/catalog/cookbook.md +389 -0
- testerkit/_docs/reference/catalog/index.md +11 -0
- testerkit/_docs/reference/catalog/schema.md +375 -0
- testerkit/_docs/reference/cli.md +660 -0
- testerkit/_docs/reference/configuration.md +406 -0
- testerkit/_docs/reference/data/channels-schema.md +145 -0
- testerkit/_docs/reference/data/event-types.md +554 -0
- testerkit/_docs/reference/data/events-schema.md +120 -0
- testerkit/_docs/reference/data/files-schema.md +141 -0
- testerkit/_docs/reference/data/index.md +18 -0
- testerkit/_docs/reference/data/models.md +2229 -0
- testerkit/_docs/reference/data/outputs.md +82 -0
- testerkit/_docs/reference/data/parquet-schema.md +529 -0
- testerkit/_docs/reference/data/performance-limits.md +224 -0
- testerkit/_docs/reference/data/query-api.md +247 -0
- testerkit/_docs/reference/index.md +69 -0
- testerkit/_docs/reference/operator-ui/channels/detail.md +82 -0
- testerkit/_docs/reference/operator-ui/channels/list.md +70 -0
- testerkit/_docs/reference/operator-ui/dashboard.md +87 -0
- testerkit/_docs/reference/operator-ui/designer.md +167 -0
- testerkit/_docs/reference/operator-ui/events.md +89 -0
- testerkit/_docs/reference/operator-ui/files.md +74 -0
- testerkit/_docs/reference/operator-ui/fixtures.md +100 -0
- testerkit/_docs/reference/operator-ui/instruments.md +126 -0
- testerkit/_docs/reference/operator-ui/launch.md +79 -0
- testerkit/_docs/reference/operator-ui/live.md +102 -0
- testerkit/_docs/reference/operator-ui/measurements.md +159 -0
- testerkit/_docs/reference/operator-ui/metrics.md +217 -0
- testerkit/_docs/reference/operator-ui/parts.md +98 -0
- testerkit/_docs/reference/operator-ui/profiles.md +103 -0
- testerkit/_docs/reference/operator-ui/results/detail.md +214 -0
- testerkit/_docs/reference/operator-ui/results/list.md +106 -0
- testerkit/_docs/reference/operator-ui/stations.md +98 -0
- testerkit/_docs/reference/operator-ui/tests.md +173 -0
- testerkit/_docs/reference/operator-ui/uuts.md +67 -0
- testerkit/_docs/reference/overview/index.md +11 -0
- testerkit/_docs/reference/overview/pytest-native.md +142 -0
- testerkit/_docs/reference/overview/skills.md +177 -0
- testerkit/_docs/reference/pytest/fixtures.md +342 -0
- testerkit/_docs/reference/pytest/index.md +12 -0
- testerkit/_docs/reference/pytest/markers.md +257 -0
- testerkit/_docs/reference/runtime/api.md +246 -0
- testerkit/_docs/reference/runtime/client.md +280 -0
- testerkit/_docs/reference/runtime/connect.md +140 -0
- testerkit/_docs/reference/runtime/index.md +13 -0
- testerkit/_docs/tutorial/01-first-test.md +113 -0
- testerkit/_docs/tutorial/02-mock-instruments.md +88 -0
- testerkit/_docs/tutorial/03-fixtures.md +169 -0
- testerkit/_docs/tutorial/04-limits.md +139 -0
- testerkit/_docs/tutorial/05-configuration.md +194 -0
- testerkit/_docs/tutorial/06-specifications.md +273 -0
- testerkit/_docs/tutorial/07-real-instruments.md +245 -0
- testerkit/_docs/tutorial/08-capabilities.md +223 -0
- testerkit/_docs/tutorial/09-production.md +374 -0
- testerkit/_docs/tutorial/10-live-monitoring.md +156 -0
- testerkit/_docs/tutorial/11-waveforms-and-evidence.md +169 -0
- testerkit/_docs/tutorial/12-continuous-monitoring.md +145 -0
- testerkit/_docs/tutorial/13-parallel-testing.md +233 -0
- testerkit/_docs/tutorial/index.md +61 -0
- testerkit/_docs/tutorial/quickstart.md +282 -0
- testerkit/analysis/__init__.py +9 -0
- testerkit/analysis/_common.py +24 -0
- testerkit/analysis/measurement_facets.py +524 -0
- testerkit/analysis/measurements_query.py +1458 -0
- testerkit/analysis/metrics.py +389 -0
- testerkit/analysis/runs_query.py +434 -0
- testerkit/analysis/steps_query.py +413 -0
- testerkit/api/__init__.py +6 -0
- testerkit/api/_mime.py +71 -0
- testerkit/api/app.py +1171 -0
- testerkit/api/dialogs/__init__.py +36 -0
- testerkit/api/dialogs/manager.py +617 -0
- testerkit/api/dialogs/models.py +79 -0
- testerkit/api/models.py +80 -0
- testerkit/api/responses.py +205 -0
- testerkit/api/runner.py +222 -0
- testerkit/api/schemas.py +311 -0
- testerkit/benchmark/__init__.py +34 -0
- testerkit/benchmark/concurrency.py +248 -0
- testerkit/benchmark/core.py +232 -0
- testerkit/benchmark/runner.py +628 -0
- testerkit/benchmark/scenario.py +313 -0
- testerkit/benchmark/system.py +191 -0
- testerkit/benchmark/workloads.py +507 -0
- testerkit/catalog/__init__.py +6 -0
- testerkit/catalog/generic/generic_dmm.yaml +61 -0
- testerkit/catalog/generic/generic_eload.yaml +31 -0
- testerkit/catalog/generic/generic_fgen.yaml +56 -0
- testerkit/catalog/generic/generic_oscilloscope.yaml +45 -0
- testerkit/catalog/generic/generic_psu.yaml +77 -0
- testerkit/catalog/generic/generic_smu.yaml +73 -0
- testerkit/channels.py +538 -0
- testerkit/cli/__init__.py +32 -0
- testerkit/cli/_common.py +113 -0
- testerkit/cli/_time.py +179 -0
- testerkit/cli/benchmark_cmd.py +56 -0
- testerkit/cli/catalog_cmd.py +39 -0
- testerkit/cli/daemon.py +157 -0
- testerkit/cli/data_cmd.py +1040 -0
- testerkit/cli/discover_cmd.py +94 -0
- testerkit/cli/docs_cmd.py +100 -0
- testerkit/cli/instrument.py +172 -0
- testerkit/cli/mcp_cmd.py +37 -0
- testerkit/cli/metrics.py +366 -0
- testerkit/cli/project.py +312 -0
- testerkit/cli/root.py +14 -0
- testerkit/cli/runs.py +412 -0
- testerkit/cli/schema_cmd.py +87 -0
- testerkit/cli/serve_cmd.py +50 -0
- testerkit/cli/setup_cmd.py +604 -0
- testerkit/cli/station.py +294 -0
- testerkit/cli/validate.py +112 -0
- testerkit/client.py +463 -0
- testerkit/connect.py +635 -0
- testerkit/data/__init__.py +8 -0
- testerkit/data/_accumulator_pool.py +455 -0
- testerkit/data/_atomic.py +59 -0
- testerkit/data/_collection_indices.py +67 -0
- testerkit/data/_daemon_lifecycle.py +535 -0
- testerkit/data/_duckdb_daemon.py +695 -0
- testerkit/data/_duckdb_flight_server.py +813 -0
- testerkit/data/_event_filters.py +15 -0
- testerkit/data/_event_reader.py +101 -0
- testerkit/data/_flight_errors.py +199 -0
- testerkit/data/_flight_query.py +202 -0
- testerkit/data/_flight_retry.py +148 -0
- testerkit/data/_flight_subscribe.py +67 -0
- testerkit/data/_index_epoch.py +342 -0
- testerkit/data/_ipc_writer.py +185 -0
- testerkit/data/_json_safe.py +63 -0
- testerkit/data/_process.py +18 -0
- testerkit/data/_push_relay.py +124 -0
- testerkit/data/_runs_duckdb_daemon.py +2704 -0
- testerkit/data/_session_reaper.py +110 -0
- testerkit/data/_sql_helpers.py +30 -0
- testerkit/data/_store.py +24 -0
- testerkit/data/backends/__init__.py +7 -0
- testerkit/data/backends/_event_accumulator.py +957 -0
- testerkit/data/backends/_row_helpers.py +1172 -0
- testerkit/data/backends/parquet.py +1143 -0
- testerkit/data/backends/protocol.py +35 -0
- testerkit/data/channels/__init__.py +8 -0
- testerkit/data/channels/_flight_daemon.py +75 -0
- testerkit/data/channels/client.py +242 -0
- testerkit/data/channels/flight_manager.py +88 -0
- testerkit/data/channels/index.py +664 -0
- testerkit/data/channels/models.py +418 -0
- testerkit/data/channels/server.py +162 -0
- testerkit/data/channels/store.py +1481 -0
- testerkit/data/data_dir.py +61 -0
- testerkit/data/duckdb_manager.py +79 -0
- testerkit/data/event_log.py +384 -0
- testerkit/data/event_store.py +768 -0
- testerkit/data/events.py +1025 -0
- testerkit/data/exporters/__init__.py +6 -0
- testerkit/data/exporters/_helpers.py +40 -0
- testerkit/data/exporters/csv_exporter.py +168 -0
- testerkit/data/exporters/hdf5.py +234 -0
- testerkit/data/exporters/json_exporter.py +229 -0
- testerkit/data/exporters/mdf4.py +201 -0
- testerkit/data/exporters/stdf.py +255 -0
- testerkit/data/exporters/tdms.py +292 -0
- testerkit/data/files/__init__.py +71 -0
- testerkit/data/files/_backend.py +207 -0
- testerkit/data/files/_catalog_daemon.py +84 -0
- testerkit/data/files/catalog.py +218 -0
- testerkit/data/files/catalog_manager.py +325 -0
- testerkit/data/files/models.py +60 -0
- testerkit/data/files/serializers.py +414 -0
- testerkit/data/files/store.py +463 -0
- testerkit/data/files/streaming.py +783 -0
- testerkit/data/models.py +604 -0
- testerkit/data/ref.py +152 -0
- testerkit/data/retention.py +297 -0
- testerkit/data/run_store.py +367 -0
- testerkit/data/runs_duckdb_manager.py +103 -0
- testerkit/data/schema_dispatch.py +191 -0
- testerkit/data/schema_migrate.py +76 -0
- testerkit/data/schema_versions.py +85 -0
- testerkit/data/schemas.py +288 -0
- testerkit/data/subscribers/__init__.py +48 -0
- testerkit/data/subscribers/_base.py +20 -0
- testerkit/data/subscribers/_output_file.py +25 -0
- testerkit/data/subscribers/replay.py +55 -0
- testerkit/environment.py +84 -0
- testerkit/execution/__init__.py +11 -0
- testerkit/execution/_git.py +190 -0
- testerkit/execution/_state.py +740 -0
- testerkit/execution/accessors.py +54 -0
- testerkit/execution/audit.py +51 -0
- testerkit/execution/cascade.py +85 -0
- testerkit/execution/connections.py +491 -0
- testerkit/execution/harness.py +1931 -0
- testerkit/execution/instrument_events.py +42 -0
- testerkit/execution/limits.py +154 -0
- testerkit/execution/metadata.py +102 -0
- testerkit/execution/mocks.py +63 -0
- testerkit/execution/profiles.py +616 -0
- testerkit/execution/run_scope.py +1331 -0
- testerkit/execution/session_scope.py +196 -0
- testerkit/execution/sidecar.py +280 -0
- testerkit/execution/site_runner.py +705 -0
- testerkit/execution/sites.py +183 -0
- testerkit/execution/sync.py +294 -0
- testerkit/execution/uut_provider.py +231 -0
- testerkit/execution/vectors.py +205 -0
- testerkit/execution/verify.py +396 -0
- testerkit/expand.py +75 -0
- testerkit/files.py +235 -0
- testerkit/fixtures/__init__.py +6 -0
- testerkit/fixtures/manager.py +357 -0
- testerkit/grafana/__init__.py +1 -0
- testerkit/grafana/bootstrap.py +61 -0
- testerkit/grafana/cli.py +314 -0
- testerkit/grafana/dashboards/asset_utilization.json +92 -0
- testerkit/grafana/dashboards/channel_explorer.json +77 -0
- testerkit/grafana/dashboards/event_log.json +92 -0
- testerkit/grafana/dashboards/failure_pareto.json +104 -0
- testerkit/grafana/dashboards/measurement_distribution.json +97 -0
- testerkit/grafana/dashboards/measurement_trend.json +161 -0
- testerkit/grafana/dashboards/station_comparison.json +95 -0
- testerkit/grafana/dashboards/test_duration.json +104 -0
- testerkit/grafana/dashboards/unit_traceability.json +73 -0
- testerkit/grafana/dashboards/yield_overview.json +189 -0
- testerkit/grafana/provisioning/dashboards.yaml.j2 +12 -0
- testerkit/grafana/provisioning/datasources.yaml.j2 +17 -0
- testerkit/grafana/server.py +282 -0
- testerkit/init.py +724 -0
- testerkit/instruments/__init__.py +10 -0
- testerkit/instruments/base.py +82 -0
- testerkit/instruments/discovery/__init__.py +62 -0
- testerkit/instruments/discovery/_base.py +174 -0
- testerkit/instruments/discovery/lxi.py +152 -0
- testerkit/instruments/discovery/ni.py +95 -0
- testerkit/instruments/discovery/serial.py +73 -0
- testerkit/instruments/discovery/visa.py +98 -0
- testerkit/instruments/lifecycle.py +213 -0
- testerkit/instruments/loader.py +79 -0
- testerkit/instruments/locks.py +178 -0
- testerkit/instruments/mocks.py +230 -0
- testerkit/instruments/observer.py +270 -0
- testerkit/instruments/observers/__init__.py +36 -0
- testerkit/instruments/observers/_base.py +68 -0
- testerkit/instruments/observers/daqmx.py +70 -0
- testerkit/instruments/observers/descriptor.py +69 -0
- testerkit/instruments/observers/generic.py +92 -0
- testerkit/instruments/observers/lantz.py +31 -0
- testerkit/instruments/observers/modbus.py +96 -0
- testerkit/instruments/observers/motion.py +68 -0
- testerkit/instruments/observers/ni_modular.py +73 -0
- testerkit/instruments/observers/ophyd.py +80 -0
- testerkit/instruments/observers/pymeasure.py +120 -0
- testerkit/instruments/observers/qcodes.py +66 -0
- testerkit/instruments/observers/scpi.py +88 -0
- testerkit/instruments/observers/tektronix.py +57 -0
- testerkit/instruments/observers/visa.py +103 -0
- testerkit/instruments/pool.py +459 -0
- testerkit/instruments/proxy.py +59 -0
- testerkit/instruments/route_manager.py +329 -0
- testerkit/instruments/routed_proxy.py +88 -0
- testerkit/instruments/server.py +508 -0
- testerkit/instruments/switch.py +43 -0
- testerkit/instruments/visa.py +294 -0
- testerkit/matching/__init__.py +6 -0
- testerkit/matching/service.py +969 -0
- testerkit/mcp/__init__.py +6 -0
- testerkit/mcp/server.py +681 -0
- testerkit/mcp/tools.py +1868 -0
- testerkit/models/__init__.py +18 -0
- testerkit/models/capability.py +598 -0
- testerkit/models/catalog.py +76 -0
- testerkit/models/data_options.py +153 -0
- testerkit/models/enums.py +334 -0
- testerkit/models/instrument.py +152 -0
- testerkit/models/instrument_asset.py +25 -0
- testerkit/models/part.py +271 -0
- testerkit/models/part_manifest.py +96 -0
- testerkit/models/project.py +114 -0
- testerkit/models/station.py +140 -0
- testerkit/models/test_config.py +748 -0
- testerkit/ontology/__init__.py +39 -0
- testerkit/ontology/schema.py +188 -0
- testerkit/ontology/testerkit.yaml +1420 -0
- testerkit/parts/__init__.py +8 -0
- testerkit/parts/context.py +181 -0
- testerkit/parts/folder.py +251 -0
- testerkit/parts/loader.py +27 -0
- testerkit/prompts/__init__.py +17 -0
- testerkit/prompts/core.py +133 -0
- testerkit/pytest_plugin/__init__.py +1287 -0
- testerkit/pytest_plugin/autouse.py +369 -0
- testerkit/pytest_plugin/helpers.py +354 -0
- testerkit/pytest_plugin/hooks.py +1982 -0
- testerkit/pytest_plugin/markers.py +203 -0
- testerkit/pytest_plugin/retry.py +37 -0
- testerkit/pytest_plugin/sweeps.py +117 -0
- testerkit/queries.py +78 -0
- testerkit/reports/__init__.py +6 -0
- testerkit/reports/core.py +492 -0
- testerkit/reports/datasheet.py +831 -0
- testerkit/reports/templates/datasheet.html +383 -0
- testerkit/reports/templates/default.html +153 -0
- testerkit/sbom.py +142 -0
- testerkit/schema_export.py +101 -0
- testerkit/signals.py +63 -0
- testerkit/skills/README.md +58 -0
- testerkit/skills/templates/project-instructions.md +125 -0
- testerkit/skills/testerkit-analysis/SKILL.md +87 -0
- testerkit/skills/testerkit-capture/SKILL.md +114 -0
- testerkit/skills/testerkit-data/SKILL.md +138 -0
- testerkit/skills/testerkit-datasheets/SKILL.md +100 -0
- testerkit/skills/testerkit-datasheets/agents/scaffold-writer.md +115 -0
- testerkit/skills/testerkit-datasheets/agents/section-extractor.md +98 -0
- testerkit/skills/testerkit-datasheets/agents/section-reviewer.md +153 -0
- testerkit/skills/testerkit-datasheets/agents/section-splitter.md +89 -0
- testerkit/skills/testerkit-datasheets/agents/section-writer.md +226 -0
- testerkit/skills/testerkit-datasheets/references/catalog-pipeline.md +271 -0
- testerkit/skills/testerkit-datasheets/references/process-queue.md +42 -0
- testerkit/skills/testerkit-datasheets/references/scaffold.md +156 -0
- testerkit/skills/testerkit-datasheets/references/test-pipeline.md +312 -0
- testerkit/skills/testerkit-debug/SKILL.md +127 -0
- testerkit/skills/testerkit-interactive/SKILL.md +188 -0
- testerkit/skills/testerkit-interactive/references/live-ui-patterns.md +102 -0
- testerkit/skills/testerkit-mocks/SKILL.md +128 -0
- testerkit/skills/testerkit-parts/SKILL.md +126 -0
- testerkit/skills/testerkit-profiles/SKILL.md +139 -0
- testerkit/skills/testerkit-sites/SKILL.md +140 -0
- testerkit/skills/testerkit-stations/SKILL.md +146 -0
- testerkit/skills/testerkit-tests/SKILL.md +136 -0
- testerkit/store.py +1639 -0
- testerkit/ui/__init__.py +71 -0
- testerkit/ui/_asgi.py +162 -0
- testerkit/ui/app.py +32 -0
- testerkit/ui/components/__init__.py +13 -0
- testerkit/ui/components/artifact_viewer.py +250 -0
- testerkit/ui/components/channel_values.py +159 -0
- testerkit/ui/components/event_timeline.py +222 -0
- testerkit/ui/components/execution_gantt.py +210 -0
- testerkit/ui/components/file_streams.py +147 -0
- testerkit/ui/components/instrument_activity.py +146 -0
- testerkit/ui/components/session_table.py +118 -0
- testerkit/ui/pages/__init__.py +25 -0
- testerkit/ui/pages/channels/__init__.py +4 -0
- testerkit/ui/pages/channels/detail.py +932 -0
- testerkit/ui/pages/channels/list.py +515 -0
- testerkit/ui/pages/dashboard.py +236 -0
- testerkit/ui/pages/designer/__init__.py +5 -0
- testerkit/ui/pages/designer/graph.py +635 -0
- testerkit/ui/pages/designer/matching.py +663 -0
- testerkit/ui/pages/designer/page.py +593 -0
- testerkit/ui/pages/designer/properties.py +591 -0
- testerkit/ui/pages/designer/state.py +468 -0
- testerkit/ui/pages/docs/__init__.py +6 -0
- testerkit/ui/pages/docs/index.py +95 -0
- testerkit/ui/pages/docs/page.py +698 -0
- testerkit/ui/pages/events/__init__.py +3 -0
- testerkit/ui/pages/events/list.py +263 -0
- testerkit/ui/pages/explore.py +1108 -0
- testerkit/ui/pages/files/__init__.py +4 -0
- testerkit/ui/pages/files/detail.py +426 -0
- testerkit/ui/pages/files/list.py +447 -0
- testerkit/ui/pages/fixtures/__init__.py +8 -0
- testerkit/ui/pages/fixtures/detail.py +280 -0
- testerkit/ui/pages/fixtures/edit.py +370 -0
- testerkit/ui/pages/fixtures/list.py +180 -0
- testerkit/ui/pages/fixtures/new.py +195 -0
- testerkit/ui/pages/instruments/__init__.py +4 -0
- testerkit/ui/pages/instruments/detail.py +320 -0
- testerkit/ui/pages/instruments/edit.py +482 -0
- testerkit/ui/pages/instruments/list.py +249 -0
- testerkit/ui/pages/instruments/new.py +200 -0
- testerkit/ui/pages/launch.py +261 -0
- testerkit/ui/pages/live.py +124 -0
- testerkit/ui/pages/metrics_page.py +1229 -0
- testerkit/ui/pages/parts/__init__.py +11 -0
- testerkit/ui/pages/parts/detail.py +202 -0
- testerkit/ui/pages/parts/edit.py +310 -0
- testerkit/ui/pages/parts/list.py +160 -0
- testerkit/ui/pages/parts/new.py +170 -0
- testerkit/ui/pages/parts/requirements.py +153 -0
- testerkit/ui/pages/parts/stations.py +199 -0
- testerkit/ui/pages/profiles/__init__.py +4 -0
- testerkit/ui/pages/profiles/detail.py +113 -0
- testerkit/ui/pages/profiles/list.py +86 -0
- testerkit/ui/pages/results/__init__.py +4 -0
- testerkit/ui/pages/results/detail.py +631 -0
- testerkit/ui/pages/results/list.py +230 -0
- testerkit/ui/pages/stations/__init__.py +4 -0
- testerkit/ui/pages/stations/detail.py +256 -0
- testerkit/ui/pages/stations/edit.py +273 -0
- testerkit/ui/pages/stations/list.py +180 -0
- testerkit/ui/pages/stations/new.py +311 -0
- testerkit/ui/pages/tests/__init__.py +4 -0
- testerkit/ui/pages/tests/detail.py +152 -0
- testerkit/ui/pages/tests/list.py +194 -0
- testerkit/ui/pages/uuts/__init__.py +4 -0
- testerkit/ui/pages/uuts/list.py +97 -0
- testerkit/ui/shared/__init__.py +8 -0
- testerkit/ui/shared/components.py +1826 -0
- testerkit/ui/shared/dialogs.py +105 -0
- testerkit/ui/shared/event_binding.py +194 -0
- testerkit/ui/shared/layout.py +314 -0
- testerkit/ui/shared/services.py +1719 -0
- testerkit/ui/shared/timestamps.py +21 -0
- testerkit/ui/static/branding/favicon-16.png +0 -0
- testerkit/ui/static/branding/favicon-32.png +0 -0
- testerkit/ui/static/branding/favicon-48.png +0 -0
- testerkit/ui/static/branding/testerkit-icon-256.png +0 -0
- testerkit/ui/static/branding/testerkit-mark.svg +1 -0
- testerkit/ui/static/branding/testerkit-wordmark.svg +1 -0
- testerkit/ui/static/global.css +465 -0
- testerkit/utils/__init__.py +17 -0
- testerkit/utils/enum_meta.py +800 -0
- testerkit/utils/paths.py +75 -0
- testerkit/utils/ranges.py +257 -0
- testerkit/validation.py +82 -0
- testerkit/verbs.py +142 -0
- testerkit-0.4.0.dist-info/METADATA +250 -0
- testerkit-0.4.0.dist-info/RECORD +521 -0
- testerkit-0.4.0.dist-info/WHEEL +4 -0
- testerkit-0.4.0.dist-info/entry_points.txt +5 -0
- testerkit-0.4.0.dist-info/licenses/LICENSE +191 -0
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
# Outcomes
|
|
2
|
+
|
|
3
|
+
Every measurement, vector, step, and run carries an `Outcome` — one of seven values — or `None` if no verdict was ever rendered. This page explains what each value means, how a worse outcome on a child rolls up to the parent, and where each value gets stamped.
|
|
4
|
+
|
|
5
|
+
For the column-by-column shape of how outcomes land in parquet, see [parquet schema → outcome values](../../reference/data/parquet-schema.md). For the level hierarchy (measurement → vector → step → run) the cascade walks, see [step hierarchy](step-hierarchy.md).
|
|
6
|
+
|
|
7
|
+
## The severity ladder
|
|
8
|
+
|
|
9
|
+
Outcomes are ordered by severity. When a parent has multiple children, the parent's outcome is the worst child's outcome — and once a parent reaches a given level, a less-severe later child doesn't weaken it.
|
|
10
|
+
|
|
11
|
+
| Severity | Value | One-liner |
|
|
12
|
+
|---:|---|---|
|
|
13
|
+
| 7 | `ABORTED` | Process died before cleanup; rig state is unknown. |
|
|
14
|
+
| 6 | `TERMINATED` | Operator stopped the run; cleanup ran; rig is safe. |
|
|
15
|
+
| 5 | `ERRORED` | Code blew up (not an assertion). |
|
|
16
|
+
| 4 | `FAILED` | A verdict ran and was violated. |
|
|
17
|
+
| 3 | `PASSED` | A verdict ran and was satisfied. |
|
|
18
|
+
| 2 | `DONE` | Code ran cleanly with no verdict — "I logged data". |
|
|
19
|
+
| 1 | `SKIPPED` | Explicit skip; the body didn't run. |
|
|
20
|
+
| — | `None` | Never judged at all. Treated as severity `-1` by the cascade. |
|
|
21
|
+
|
|
22
|
+
### Cascade rule
|
|
23
|
+
|
|
24
|
+
The cascade returns the higher-severity of the two. **Ties favor the current outcome** — once a parent reaches FAILED, an incoming FAILED leaves it unchanged. This matters when interpreting timestamps: the first FAILED stamp is the one that survives.
|
|
25
|
+
|
|
26
|
+
`None` participates with severity `-1`, so any real outcome wins against `None`.
|
|
27
|
+
|
|
28
|
+
## Verdict intent — what separates PASSED from DONE
|
|
29
|
+
|
|
30
|
+
The difference between PASSED and DONE on a step that ran cleanly: did the body *try* to judge?
|
|
31
|
+
|
|
32
|
+
A step has **verdict intent** if either fires during the test:
|
|
33
|
+
|
|
34
|
+
- A passing `assert` ran in the test body.
|
|
35
|
+
- A measurement with a limit was recorded — the limit is the thing that gets judged.
|
|
36
|
+
|
|
37
|
+
At step end, the plugin picks PASSED if the step had any verdict intent, DONE if not.
|
|
38
|
+
|
|
39
|
+
So:
|
|
40
|
+
|
|
41
|
+
- Test body exits cleanly **with** verdict intent → **PASSED**.
|
|
42
|
+
- Test body exits cleanly **without** verdict intent → **DONE**.
|
|
43
|
+
- Test body raises `AssertionError` → **FAILED**.
|
|
44
|
+
- Test body raises anything else → **ERRORED**.
|
|
45
|
+
- Test body raises `pytest.skip.Exception` → **SKIPPED**.
|
|
46
|
+
|
|
47
|
+
## What each outcome means
|
|
48
|
+
|
|
49
|
+
### `PASSED` — a verdict ran and was satisfied
|
|
50
|
+
|
|
51
|
+
- **Measurement-level**: value was checked against a limit and was in range (`value in limit` returned `True`).
|
|
52
|
+
- **Step-level**: the test body exited cleanly AND verdict intent fired at least once.
|
|
53
|
+
- **Run-level**: rolls up from PASSED steps with nothing worse anywhere.
|
|
54
|
+
|
|
55
|
+
### `FAILED` — a verdict ran and was violated
|
|
56
|
+
|
|
57
|
+
- **Measurement-level**: value was checked and `value in limit` returned `False`.
|
|
58
|
+
- **Step-level**: the test body raised `AssertionError` (rewritten or bare), OR a contained measurement landed FAILED and cascaded up.
|
|
59
|
+
- **Run-level**: rolls up from any FAILED step.
|
|
60
|
+
|
|
61
|
+
### `DONE` — clean run, no verdict
|
|
62
|
+
|
|
63
|
+
The "I logged data" outcome. Not a "good" outcome and not a "bad" one — judgment never happened.
|
|
64
|
+
|
|
65
|
+
- **Measurement-level**: a value was recorded with no `low`/`high`/`nominal` (no limit to check against).
|
|
66
|
+
- **Step-level**: the body exited cleanly AND no verdict intent fired.
|
|
67
|
+
- **Run-level**: rolls up from DONE steps with nothing worse.
|
|
68
|
+
|
|
69
|
+
### `SKIPPED` — explicit skip
|
|
70
|
+
|
|
71
|
+
- **Step-level**: `pytest.skip(...)`, `@pytest.mark.skip`, `@pytest.mark.skipif`, or a setup-phase skip exception. The test body either didn't run or stopped early.
|
|
72
|
+
- **Run-level**: cascade rollup where the only contained outcomes were SKIPPED.
|
|
73
|
+
- **Vector-level**: `VectorBuilder.skip(...)` on the `TesterKitClient` builder path explicitly stamps SKIPPED. Not produced by the runtime cascade.
|
|
74
|
+
|
|
75
|
+
### `ERRORED` — unhandled exception
|
|
76
|
+
|
|
77
|
+
Two distinct paths land here, and they're not interchangeable:
|
|
78
|
+
|
|
79
|
+
- **Step-level**: the test body (or setup / teardown) raised any non-`AssertionError`, non-skip exception. A `ValueError`, `RuntimeError`, `pyvisa.VisaIOError`, etc. **No `Measurement` row is recorded for the broken call** — the step is ERRORED, not the (non-existent) measurement.
|
|
80
|
+
- **Measurement-level**: the row exists, with `value=None`. Happens when:
|
|
81
|
+
- `verify("vout", instr.measure_voltage())` was called and `measure_voltage()` *returned* None silently (broken driver, mock not configured, swallowed timeout).
|
|
82
|
+
|
|
83
|
+
Exceptions do not produce ERRORED measurements — they produce a step-level ERRORED with no measurement row.
|
|
84
|
+
|
|
85
|
+
- **Run-level**: rolls up from any ERRORED step or measurement.
|
|
86
|
+
|
|
87
|
+
### `TERMINATED` — operator stopped cleanly
|
|
88
|
+
|
|
89
|
+
The operator stopped the run (Ctrl-C or SIGTERM). Cleanup ran to completion — fixture teardowns finished, instruments went to a safe state, and results were saved.
|
|
90
|
+
|
|
91
|
+
The rig **is** in a known state. The run was stopped on purpose, with cleanup. Downstream tooling and operator runbooks can read TERMINATED as "intentional stop; rig safe."
|
|
92
|
+
|
|
93
|
+
### `ABORTED` — process died before cleanup
|
|
94
|
+
|
|
95
|
+
The runs daemon was asked to write a run that never saw a `RunEnded` event. The teardown chain didn't complete — the run never finalized.
|
|
96
|
+
|
|
97
|
+
- The process was killed mid-flight (SIGKILL, segfault, OOM kill, host shutdown).
|
|
98
|
+
- An exception bypassed teardown before finalization could run.
|
|
99
|
+
|
|
100
|
+
The rig state is **unknown**. When a run is killed before it finalizes, TesterKit records the unfinished run as ABORTED automatically — so a process that dies mid-run is never silently lost. A non-pytest runner can also mark a run ABORTED through the results API; see the [client reference](../../reference/runtime/client.md).
|
|
101
|
+
|
|
102
|
+
ABORTED on a parquet row means the run never closed cleanly — downstream tooling and operator runbooks should treat the rig as "physically inspect required."
|
|
103
|
+
|
|
104
|
+
### `None` — never judged, never finalized
|
|
105
|
+
|
|
106
|
+
The row exists (it was collected, or a step was opened) but no outcome was ever set.
|
|
107
|
+
|
|
108
|
+
- A pytest test that pytest collected but never ran — earlier failure aborted the session, or `--exitfirst` cut things short.
|
|
109
|
+
- A vector that ran but recorded nothing and didn't raise.
|
|
110
|
+
|
|
111
|
+
A finalized run whose row has no outcome simply never reached a verdict. The operator UI shows these as "Never Ran" — see [step manifest](step-manifest.md#never-ran).
|
|
112
|
+
|
|
113
|
+
## Where each outcome gets stamped
|
|
114
|
+
|
|
115
|
+
The tables below list what triggers each outcome at each level, worst → least severe.
|
|
116
|
+
|
|
117
|
+
### Measurement level
|
|
118
|
+
|
|
119
|
+
A measurement has no outcome until its value is checked against a limit.
|
|
120
|
+
|
|
121
|
+
| Outcome | Triggering conditions |
|
|
122
|
+
|---|---|
|
|
123
|
+
| `ABORTED` | *(never produced at this level)* |
|
|
124
|
+
| `TERMINATED` | *(never produced at this level)* |
|
|
125
|
+
| `ERRORED` | A `None` value reached the check — typically a driver returned `None` silently (e.g. `verify("vout", None)`) |
|
|
126
|
+
| `FAILED` | Value checked against its limit and out of range |
|
|
127
|
+
| `PASSED` | Value checked against its limit and in range |
|
|
128
|
+
| `DONE` | Value recorded with no limit to judge against — e.g. `measure(...)` with no limit |
|
|
129
|
+
| `SKIPPED` | *(never produced at this level)* |
|
|
130
|
+
| `None` | Default; row constructed but limit check not invoked |
|
|
131
|
+
|
|
132
|
+
An exception in a called function (e.g. driver raises a VISA timeout) does **not** produce an ERRORED measurement — it produces no measurement record at all, and the enclosing step lands ERRORED instead.
|
|
133
|
+
|
|
134
|
+
### Vector level
|
|
135
|
+
|
|
136
|
+
`TestVector.outcome` defaults to `None`.
|
|
137
|
+
|
|
138
|
+
| Outcome | Triggering conditions |
|
|
139
|
+
|---|---|
|
|
140
|
+
| `ABORTED` | *(never produced at this level)* |
|
|
141
|
+
| `TERMINATED` | *(never produced at this level)* |
|
|
142
|
+
| `ERRORED` | Rolls up from an ERRORED measurement; or the vector body raised a non-`AssertionError` exception |
|
|
143
|
+
| `FAILED` | Rolls up from a FAILED measurement; or the vector body raised `AssertionError`; or `VectorBuilder.fail(...)` was called (results-API path) |
|
|
144
|
+
| `PASSED` | Rolls up from a PASSED measurement |
|
|
145
|
+
| `DONE` | Rolls up from a recorded-but-unjudged measurement |
|
|
146
|
+
| `SKIPPED` | `VectorBuilder.skip(...)` explicit call (`TesterKitClient` builder path only — not produced by the runtime cascade) |
|
|
147
|
+
| `None` | Default; vector ran but recorded nothing and didn't raise |
|
|
148
|
+
|
|
149
|
+
### Step level
|
|
150
|
+
|
|
151
|
+
A step's outcome rolls up from its measurements and from how the test body ended; once a worse outcome is set, a less-severe later result doesn't weaken it.
|
|
152
|
+
|
|
153
|
+
| Outcome | Triggering conditions |
|
|
154
|
+
|---|---|
|
|
155
|
+
| `ABORTED` | *(never produced at this level — see Run level)* |
|
|
156
|
+
| `TERMINATED` | Operator stopped the run (Ctrl-C or SIGTERM) |
|
|
157
|
+
| `ERRORED` | Test body raised any non-`AssertionError`, non-skip exception; setup or teardown raised any non-skip exception; or rolls up from an ERRORED measurement or vector |
|
|
158
|
+
| `FAILED` | Test body raised `AssertionError`; or rolls up from a FAILED measurement or vector; or `StepBuilder.fail(...)` was called (results-API path) |
|
|
159
|
+
| `PASSED` | Test body exited cleanly AND verdict intent was recorded for this step; or rolls up from a PASSED measurement with nothing worse |
|
|
160
|
+
| `DONE` | Test body exited cleanly with **no** verdict intent (no asserts ran, no measurements with limits) |
|
|
161
|
+
| `SKIPPED` | Test body raised `pytest.skip.Exception` (`pytest.skip(...)`, `@pytest.mark.skip`, `skipif`); setup-phase skip exception (e.g. `skipif` from a fixture); `StepBuilder.skip(...)` explicit call |
|
|
162
|
+
| `None` | Default; the step opened but never ran (e.g. an upstream failure stopped the session) |
|
|
163
|
+
|
|
164
|
+
### Run level
|
|
165
|
+
|
|
166
|
+
A run's outcome rolls up through every measurement and step; it's written to the run's saved record when the run ends.
|
|
167
|
+
|
|
168
|
+
| Outcome | Triggering conditions |
|
|
169
|
+
|---|---|
|
|
170
|
+
| `ABORTED` | The run was killed before it finalized; TesterKit records the unfinished run as ABORTED automatically. A non-pytest runner can also mark a run ABORTED via the results API. |
|
|
171
|
+
| `TERMINATED` | Rolls up from any step that landed TERMINATED |
|
|
172
|
+
| `ERRORED` | Rolls up from any ERRORED step or measurement |
|
|
173
|
+
| `FAILED` | Rolls up from any FAILED step or measurement |
|
|
174
|
+
| `PASSED` | Rolls up from PASSED step(s) with nothing worse anywhere |
|
|
175
|
+
| `DONE` | Rolls up from step(s) that ran cleanly without verdict intent |
|
|
176
|
+
| `SKIPPED` | Cascade rollup where the only contained outcomes were SKIPPED |
|
|
177
|
+
| `None` | Default; a run that ended without any outcome being set. A run killed before it finalizes is recorded as ABORTED instead (see top row). |
|
|
178
|
+
|
|
179
|
+
### Multi-UUT site orchestrator
|
|
180
|
+
|
|
181
|
+
In a multi-UUT run, each site (one DUT) gets its own result; the session's overall result is the worst across sites.
|
|
182
|
+
|
|
183
|
+
| Site result | Triggering condition |
|
|
184
|
+
|---|---|
|
|
185
|
+
| `errored` | The site's run never finished cleanly (killed, orphaned) |
|
|
186
|
+
| `failed` | The site's run finished with a failure |
|
|
187
|
+
| `passed` | The site's run finished clean |
|
|
188
|
+
|
|
189
|
+
Each DUT still records its own detailed outcome from the per-run tables above. For the operational guide, see [multi-UUT testing](../../how-to/execution/multi-uut-testing.md).
|
|
190
|
+
|
|
191
|
+
## See also
|
|
192
|
+
|
|
193
|
+
- [Step hierarchy](step-hierarchy.md) — the measurement / vector / step / run levels the cascade walks
|
|
194
|
+
- [Step manifest](step-manifest.md) — how `outcome IS NULL` rows show as "Never Ran" in finalized runs
|
|
195
|
+
- [Event log](../data/event-log.md) — `RunEnded` / `StepEnded` events that carry the cascade-derived outcome
|
|
196
|
+
- [Multi-UUT testing](../../how-to/execution/multi-uut-testing.md) — site orchestrator outcomes in practice
|
|
197
|
+
- [Limits](../../how-to/execution/limits.md) — how a measurement gets a limit attached (the trigger for measurement-level PASSED/FAILED and for step-level verdict intent)
|
|
198
|
+
- [Models reference](../../reference/data/models.md) — `Outcome` enum source-of-truth and field tables for `Measurement` / `TestVector` / `TestStep` / `TestRun`
|
|
199
|
+
- [Parquet schema](../../reference/data/parquet-schema.md) — column-level definitions of `run_outcome`, `step_outcome`, `measurement_outcome`
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
# Step Hierarchy — runs, steps, vectors, measurements
|
|
2
|
+
|
|
3
|
+
This page is the single reference for TesterKit's run-data hierarchy: what each level represents, how the levels nest, and how they're identified in the event log and the materialized tables. Pair it with [Outcomes](outcomes.md) for what each level's verdict means and where it gets set, and [Step Manifest](step-manifest.md) for the planned-vs-executed reconciliation.
|
|
4
|
+
|
|
5
|
+
## The hierarchy
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
TestRun ← one per pytest session
|
|
9
|
+
└── Step ← class container (one per class; one per outer iteration if the class is swept)
|
|
10
|
+
└── Step ← test method (one per pytest item)
|
|
11
|
+
└── TestVector ← one per inner iteration (1 for normal swept tests; N for `vectors`-fixture tests)
|
|
12
|
+
└── Measurement ← one per `measure` / `verify` call
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Each level emits its own event in the run log. Each level rolls its outcome up to the next level via the severity-max ladder (see [Outcomes](outcomes.md)). `verify` and `measure` are pytest [fixtures](../../reference/pytest/fixtures.md); `vectors` is the [self-loop fixture](../../how-to/execution/vector-expansion.md).
|
|
16
|
+
|
|
17
|
+
## What each level is
|
|
18
|
+
|
|
19
|
+
### TestRun
|
|
20
|
+
|
|
21
|
+
One run = one pytest session. Wraps a session_id, run_id, uut_serial_number, station, fixture, operator — all of it is the run-level context.
|
|
22
|
+
|
|
23
|
+
Events: `RunStarted` at session start, `RunEnded` at session end. The session also emits `SessionStarted` / `SessionEnded`, but those are session-scoped (could span multiple runs in a multi-site harness).
|
|
24
|
+
|
|
25
|
+
### Step — the unit of "one thing the test did"
|
|
26
|
+
|
|
27
|
+
A step is a named, ordered unit. Two kinds, but they share one event type and one shape:
|
|
28
|
+
|
|
29
|
+
1. **Container step (class container).** Synthesized by the pytest plugin when execution enters a test class. Methods inside the class push beneath it on the step stack. When the class is swept (class-level `@pytest.mark.testerkit_sweeps`), one container step is emitted **per outer iteration** — each with its own `vector_index` and `inputs={outer_param: value}`.
|
|
30
|
+
|
|
31
|
+
2. **Method step.** One per pytest-collected item. The test function's body is the step's work.
|
|
32
|
+
|
|
33
|
+
Whether a step is a class container or a method isn't a separate flag — it's implied by the nesting: a step is a container when at least one other step's `step_path` starts with it as a prefix.
|
|
34
|
+
|
|
35
|
+
Events: `StepStarted` when the step opens, `StepEnded` when it closes. `step_path` encodes the full hierarchy.
|
|
36
|
+
|
|
37
|
+
Identity: `(step_path, vector_index)` is unique per executed step instance within a run. For a method run as 3 parametrize variants, you get 3 `StepStarted` events with the same `step_path` (e.g., `"TestPower/test_voltage"`) and distinct `vector_index` 0/1/2.
|
|
38
|
+
|
|
39
|
+
### TestVector — one inner iteration
|
|
40
|
+
|
|
41
|
+
For normal swept tests (one pytest item per variant), each step has exactly one TestVector. The vector carries the sweep parameter values (`vin=5.0`) and ends up as that step's `inputs`.
|
|
42
|
+
|
|
43
|
+
For tests using the `vectors` fixture, the test body iterates the matrix itself. Each iteration appends a new TestVector to the step. The step has one outer identity, but N internal vectors and N measurements.
|
|
44
|
+
|
|
45
|
+
### Measurement — one recorded value
|
|
46
|
+
|
|
47
|
+
A `measure("vin_voltage", 3.30)` or a `verify(...)` call. Carries the value, units, limit, characteristic_id, and uut_pin / instrument_resource / fixture_connection traceability fields.
|
|
48
|
+
|
|
49
|
+
Events: `MeasurementRecorded`. Carries the full effective `inputs` dict — outer step params **merged with** the current vector's inner params — so analytics queries can filter on either dimension without joining back to the step.
|
|
50
|
+
|
|
51
|
+
## Identity fields
|
|
52
|
+
|
|
53
|
+
| Field | Where it's set | Used for |
|
|
54
|
+
|---|---|---|
|
|
55
|
+
| `step_path` | Built from the chain of enclosing steps (e.g., `TestPower/test_voltage`) | Hierarchical identity; parent derivable as `step_path.rsplit("/", 1)[0]` |
|
|
56
|
+
| `step_index` | Assigned to each step before the run, when tests are collected | Sequence-relative ordering within a parent bucket |
|
|
57
|
+
| `vector_index` | Pre-assigned at collection time for swept items; 0 for plain steps | Distinguishes sweep variants of the same logical step |
|
|
58
|
+
| `step_name` | The function or class name | Display |
|
|
59
|
+
| `inputs` (on `StepStarted`) | Outer sweep params from `callspec.params` | Step row's commanded conditions |
|
|
60
|
+
| `inputs` (on `MeasurementRecorded`) | Outer step inputs + active vector params | Full per-row sweep context |
|
|
61
|
+
|
|
62
|
+
`(step_path, vector_index)` is the per-step-instance identity. For container steps it's `(class_name, outer_iteration_index)`; for method steps it's `(class_name/method_name, per-method-counter)`.
|
|
63
|
+
|
|
64
|
+
## Worked example — swept class with method-level inner sweep
|
|
65
|
+
|
|
66
|
+
```python
|
|
67
|
+
@pytest.mark.testerkit_sweeps([{"voltage": [1, 2, 3]}]) # class-level → outer
|
|
68
|
+
class TestPower:
|
|
69
|
+
def test_warmup(self, voltage, measure):
|
|
70
|
+
measure("vin_warmup", voltage)
|
|
71
|
+
|
|
72
|
+
@pytest.mark.testerkit_sweeps([{"current": [4, 5, 6]}]) # method-level → inner
|
|
73
|
+
def test_load(self, voltage, current, measure):
|
|
74
|
+
measure("vout_load", voltage * 1.1)
|
|
75
|
+
|
|
76
|
+
def test_cooldown(self, voltage, measure):
|
|
77
|
+
measure("vin_cooldown", 0)
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Event stream (condition-first):
|
|
81
|
+
|
|
82
|
+
```
|
|
83
|
+
StepStarted ("TestPower", vi=0, inputs={voltage:1})
|
|
84
|
+
StepStarted ("test_warmup", vi=0, inputs={voltage:1})
|
|
85
|
+
MeasurementRecorded("vin_warmup", inputs={voltage:1})
|
|
86
|
+
StepEnded ("test_warmup", vi=0)
|
|
87
|
+
StepStarted ("test_load", vi=0, inputs={voltage:1, current:4})
|
|
88
|
+
MeasurementRecorded("vout_load", inputs={voltage:1, current:4})
|
|
89
|
+
StepEnded ("test_load", vi=0)
|
|
90
|
+
StepStarted ("test_load", vi=1, inputs={voltage:1, current:5})
|
|
91
|
+
...
|
|
92
|
+
StepStarted ("test_cooldown", vi=0, inputs={voltage:1})
|
|
93
|
+
...
|
|
94
|
+
StepEnded ("TestPower", vi=0, outcome=<rolled-up>)
|
|
95
|
+
StepStarted ("TestPower", vi=1, inputs={voltage:2}, ...)
|
|
96
|
+
...
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
3 container iterations × (3 methods, with `test_load` unrolling to 3 inner variants) = 15 method `StepStarted` events under 3 container iterations.
|
|
100
|
+
|
|
101
|
+
`step_index` for the container is 0 (root-level), for each method is 0/1/2 within the `TestPower` class bucket — so `(step_index=1, vector_index=0)` uniquely points to `test_load[voltage=1, current=4]`.
|
|
102
|
+
|
|
103
|
+
## `vectors` fixture — one step, many in-body vectors (Mode 2)
|
|
104
|
+
|
|
105
|
+
When the method uses the `vectors` fixture, pytest sees ONE item per outer iteration. The step has ONE outer identity (matching the outer-iteration position) and N in-body vector iterations, each bracketed by `VectorStarted` / `VectorEnded` events:
|
|
106
|
+
|
|
107
|
+
```python
|
|
108
|
+
@pytest.mark.testerkit_sweeps([{"voltage": [1, 2, 3]}])
|
|
109
|
+
class TestPower:
|
|
110
|
+
@pytest.mark.testerkit_sweeps([{"current": [4, 5, 6]}])
|
|
111
|
+
def test_load(self, voltage, vectors, measure):
|
|
112
|
+
for v in vectors:
|
|
113
|
+
measure("vout", voltage * v["current"])
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Event stream:
|
|
117
|
+
|
|
118
|
+
```
|
|
119
|
+
StepStarted ("test_load", vi=0, inputs={voltage:1}) # outer position
|
|
120
|
+
VectorStarted ("test_load", vi=0, inputs={voltage:1, current:4})
|
|
121
|
+
MeasurementRecorded("vout", inputs={voltage:1, current:4})
|
|
122
|
+
VectorEnded ("test_load", vi=0, outcome=passed)
|
|
123
|
+
VectorStarted ("test_load", vi=1, inputs={voltage:1, current:5})
|
|
124
|
+
MeasurementRecorded("vout", inputs={voltage:1, current:5})
|
|
125
|
+
VectorEnded ("test_load", vi=1, outcome=passed)
|
|
126
|
+
VectorStarted ("test_load", vi=2, inputs={voltage:1, current:6})
|
|
127
|
+
MeasurementRecorded("vout", inputs={voltage:1, current:6})
|
|
128
|
+
VectorEnded ("test_load", vi=2, outcome=passed)
|
|
129
|
+
StepEnded ("test_load", vi=0, outcome=<rolled-up>)
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
`StepStarted.vector_index` and `StepEnded.vector_index` agree (the outer iteration position). Each `VectorStarted`/`VectorEnded` pair is the in-body analog of a Mode-1 step boundary — it brackets one iteration's work and carries the full effective `inputs` for that iteration. In the materialized parquet, each `VectorStarted`/`VectorEnded` pair produces one `record_type = 'vector'` row; `record_type = 'step'` rows are NOT emitted for Mode-2 in-body iterations (only for the enclosing step itself).
|
|
133
|
+
|
|
134
|
+
## Outcome rollup chain
|
|
135
|
+
|
|
136
|
+
```
|
|
137
|
+
measurement.outcome
|
|
138
|
+
↓ escalate (severity-max ladder)
|
|
139
|
+
TestVector.outcome (per inner iteration)
|
|
140
|
+
↓ escalate
|
|
141
|
+
StepEnded.outcome (the method's aggregate verdict)
|
|
142
|
+
↓ escalate (the container takes the worst verdict among its children)
|
|
143
|
+
container.outcome (the class iteration's verdict, on its StepEnded)
|
|
144
|
+
↓ escalate
|
|
145
|
+
TestRun.outcome (the run's overall verdict, on RunEnded)
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
Severity ladder: `ABORTED > TERMINATED > ERRORED > FAILED > PASSED > DONE > SKIPPED`. Worst wins at every level. Full mapping and producer sites in [Outcomes](outcomes.md).
|
|
149
|
+
|
|
150
|
+
## Materialized record identity
|
|
151
|
+
|
|
152
|
+
The at-rest per-run parquet contains three `record_type` values: `run`, `step`, and `vector` (querying also gives you a `measurement` record type, expanded from the measurements inside each vector). Container steps and method steps share the `step` record type — tell them apart by `step_path` depth. The parent of any step is derivable as `step_path.rsplit("/", 1)[0] if "/" in step_path else ""`:
|
|
153
|
+
|
|
154
|
+
- no `/` in `step_path` → root-level (run-level test functions or class containers)
|
|
155
|
+
- one `/` in `step_path` → method directly under a class container (e.g. `TestPower/test_voltage`)
|
|
156
|
+
- two or more `/` → nested step (via `harness.step()` self-loops)
|
|
157
|
+
|
|
158
|
+
`vector` records appear only for Mode-2 in-body iterations (`vectors` fixture). They key on `(step_path, vector_index, retry)` and sit below their enclosing `step` row. Mode-1 steps (parametrize / single) have no `vector` rows — the `step` row already carries the vector data.
|
|
159
|
+
|
|
160
|
+
`MAX(severity)` over `step` rows sharing a `step_path` aggregates "did this class ever fail in this run" across its iterations. See the [results storage reference](../../reference/data/parquet-schema.md) for the full column schema.
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
## See also
|
|
164
|
+
|
|
165
|
+
**Related quadrants:**
|
|
166
|
+
|
|
167
|
+
- [How-to → Execution](../../how-to/execution/index.md) — how-to entry point for this category
|
|
168
|
+
- [Reference](../../reference/index.md) — reference entry point for this category
|
|
169
|
+
- [Integration](../../integration/index.md) — integration entry point for this category
|
|
170
|
+
- [Tutorial](../../tutorial/index.md) — tutorial entry point for this category
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# Step Results & the Step Manifest
|
|
2
|
+
|
|
3
|
+
The step manifest is the full list of planned steps for a run — including the ones that never ran. Step results give a complete view of every planned test step (early abort, `--maxfail`, skip markers).
|
|
4
|
+
|
|
5
|
+
## Why record planned steps
|
|
6
|
+
|
|
7
|
+
Without explicit step records, you only know about steps that actually executed. If a run aborts after step 3 of 10, the Parquet file would only have 3 rows of evidence. There's no record that 7 other steps were planned but never ran.
|
|
8
|
+
|
|
9
|
+
That matters for:
|
|
10
|
+
|
|
11
|
+
- **Yield analysis** — A 3/3 pass result is not the same as a 3/10 partial result.
|
|
12
|
+
- **Coverage tracking** — Which steps are consistently skipped or never reached?
|
|
13
|
+
- **Compliance** — Auditors need to know the full test plan, not just what ran.
|
|
14
|
+
|
|
15
|
+
## StepsDiscovered event
|
|
16
|
+
|
|
17
|
+
The `StepsDiscovered` event fires after instruments connect but before any steps execute. It carries the complete list of pytest-collected items:
|
|
18
|
+
|
|
19
|
+
```python
|
|
20
|
+
class StepsDiscovered(EventBase):
|
|
21
|
+
event_type: Literal["test.steps_discovered"] = "test.steps_discovered"
|
|
22
|
+
items: list[dict[str, str | int | None]] = Field(default_factory=list)
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Each item in `items` contains the pytest identity and collection-time manifest data:
|
|
26
|
+
|
|
27
|
+
| Field | Description |
|
|
28
|
+
|-------|-------------|
|
|
29
|
+
| `node_id` | The step's pytest address — file plus test name, e.g. `tests/test_power.py::test_voltage` |
|
|
30
|
+
| `file` | Source file path |
|
|
31
|
+
| `module` | Python module name |
|
|
32
|
+
| `class_name` | Test class name (if any) |
|
|
33
|
+
| `function` | Test function name |
|
|
34
|
+
| `markers` | pytest markers on the item |
|
|
35
|
+
| `step_path` | Hierarchical step identifier, matching executed step events |
|
|
36
|
+
| `step_index` | Position within the parent sequence |
|
|
37
|
+
| `vector_index` | 0-based position within the sweep expansion |
|
|
38
|
+
| `vector_count_planned` | Total vectors collected for this logical step — drives placeholder row synthesis for unrun vectors |
|
|
39
|
+
|
|
40
|
+
## How it flows
|
|
41
|
+
|
|
42
|
+
When the run ends, TesterKit writes one row per planned step — executed steps with their real outcome and timing, and a placeholder row for any step that never started.
|
|
43
|
+
|
|
44
|
+
## Storage
|
|
45
|
+
|
|
46
|
+
There is **one parquet file per run**. Run, step, and vector records share the same file; the [`record_type`](../../reference/data/parquet-schema.md) column says which kind each row is. Measurements are stored inside each vector row:
|
|
47
|
+
|
|
48
|
+
```
|
|
49
|
+
<data_dir>/runs/{date}/
|
|
50
|
+
└── {timestamp}_{serial}.parquet # All rows for one run
|
|
51
|
+
├── record_type='run' # exactly one row, run-level metadata
|
|
52
|
+
├── record_type='step' # one row per (step_path, vector_index, retry)
|
|
53
|
+
└── record_type='vector' # one row per execution; nests the measurements list
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Key step-row columns (full list in [Parquet schema](../../reference/data/parquet-schema.md)):
|
|
57
|
+
|
|
58
|
+
- `step_name`, `step_path`, `step_index`, `step_node_id`
|
|
59
|
+
- `step_started_at`, `step_ended_at`, `step_vector_count`
|
|
60
|
+
- `step_outcome` (rollup), `vector_outcome` (per vector), `run_outcome` (run-wide)
|
|
61
|
+
- Run context repeated on every step row (so a step row is self-contained): `run_id`, `uut_serial_number`, `station_id`, `session_id`
|
|
62
|
+
|
|
63
|
+
## Steps that never ran {#never-ran}
|
|
64
|
+
|
|
65
|
+
A run can finish before every planned step executes — an early abort, `--maxfail`, or a skip. TesterKit still records those steps so the run shows the full plan, not just what ran. Each unrun step gets a placeholder row with a blank outcome and no start/end time (query hint: `step_outcome IS NULL`).
|
|
66
|
+
|
|
67
|
+
- `step_outcome`: NULL
|
|
68
|
+
- Timing fields: NULL
|
|
69
|
+
- Step identity columns: populated from the collected item
|
|
70
|
+
|
|
71
|
+
Every run thus has a complete picture — executed steps with real data, plus placeholder rows for the rest.
|
|
72
|
+
|
|
73
|
+
## Querying step results
|
|
74
|
+
|
|
75
|
+
With DuckDB:
|
|
76
|
+
|
|
77
|
+
```sql
|
|
78
|
+
-- Step summary for one run
|
|
79
|
+
SELECT step_name, step_outcome, step_started_at, step_ended_at
|
|
80
|
+
FROM read_parquet('data/runs/**/*.parquet')
|
|
81
|
+
WHERE record_type = 'step'
|
|
82
|
+
AND run_id = 'abc123'
|
|
83
|
+
ORDER BY step_index;
|
|
84
|
+
|
|
85
|
+
-- Find steps that are frequently skipped or never run
|
|
86
|
+
SELECT step_name,
|
|
87
|
+
COUNT(*) AS total,
|
|
88
|
+
SUM(CASE WHEN step_outcome IS NULL THEN 1 ELSE 0 END) AS never_ran
|
|
89
|
+
FROM read_parquet('data/runs/**/*.parquet')
|
|
90
|
+
WHERE record_type = 'step'
|
|
91
|
+
GROUP BY step_name
|
|
92
|
+
HAVING never_ran > 0;
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
From Python (via `RunStore`):
|
|
96
|
+
|
|
97
|
+
```python
|
|
98
|
+
from testerkit.data.run_store import RunStore
|
|
99
|
+
|
|
100
|
+
steps = RunStore().get_steps("abc123")
|
|
101
|
+
for step in steps:
|
|
102
|
+
print(f"{step['step_name']}: {step['outcome']}")
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
From the event store:
|
|
106
|
+
|
|
107
|
+
```python
|
|
108
|
+
store.events(event_type="test.steps_discovered", session_id=sid)
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## See also
|
|
112
|
+
|
|
113
|
+
- [Event log](../data/event-log.md) — how events get to Parquet
|
|
114
|
+
- [Parquet schema](../../reference/data/parquet-schema.md) — full column list
|
|
115
|
+
- [Data stores](../data/data-stores.md) — EventStore, ChannelStore, FileStore, RunStore
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# Concepts
|
|
2
|
+
|
|
3
|
+
Why TesterKit is built the way it is. Concepts are grouped by what you're trying to understand. Each group has its own index; jump to the page you care about, or read top-to-bottom for the full mental model.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
[Overview index →](overview/index.md). The platform's mental model from above — what TesterKit is, what it isn't, and why pytest sits underneath.
|
|
8
|
+
|
|
9
|
+
- [Architecture](overview/architecture.md) — system-level view of parts, stations, fixtures, and runs
|
|
10
|
+
- [Platform vs framework](overview/platform-vs-framework.md) — what the platform owns vs what the runner owns
|
|
11
|
+
- [pytest](overview/pytest.md) — why a hardware-test platform rides on pytest instead of its own runner
|
|
12
|
+
- [AI integration](overview/ai-integration.md) — what the MCP surface buys you and where it draws the line
|
|
13
|
+
|
|
14
|
+
## Configuration
|
|
15
|
+
|
|
16
|
+
[Configuration index →](configuration/index.md). The UUT-to-instrument model — the YAML entities you author once and re-use across runs.
|
|
17
|
+
|
|
18
|
+
- [Parts](configuration/parts.md) — what you're testing
|
|
19
|
+
- [Stations](configuration/stations.md) — where you test
|
|
20
|
+
- [Capabilities](configuration/capabilities.md) — what instruments can do, how matching pairs them with part characteristics
|
|
21
|
+
- [Fixtures](configuration/fixtures.md) — pin-to-instrument mapping
|
|
22
|
+
|
|
23
|
+
## Execution
|
|
24
|
+
|
|
25
|
+
[Execution index →](execution/index.md). How a test run unfolds — the step model, the outcome ladder, what each step records.
|
|
26
|
+
|
|
27
|
+
- [Step hierarchy](execution/step-hierarchy.md) — how test classes, methods, and vectors nest
|
|
28
|
+
- [Step manifest](execution/step-manifest.md) — what each step records
|
|
29
|
+
- [Outcomes](execution/outcomes.md) — skipped / done / passed / failed / errored / terminated / aborted severity ladder
|
|
30
|
+
|
|
31
|
+
## Data
|
|
32
|
+
|
|
33
|
+
[Data index →](data/index.md). Where the run data lives and how the platform stays consistent across processes.
|
|
34
|
+
|
|
35
|
+
- [Event log](data/event-log.md) — the durable record of every run
|
|
36
|
+
- [Event sourcing](data/event-sourcing.md) — append-only event log as the source of truth
|
|
37
|
+
- [Sessions](data/sessions.md) — connect-to-disconnect observation windows for instrument use
|
|
38
|
+
- [Three verbs](data/three-verbs.md) — `observe` / `verify` / `stream`; routing a value to the right store by shape
|
|
39
|
+
- [Data stores](data/data-stores.md) — EventStore, ChannelStore, FileStore, RunStore; on-disk layout, data_dir resolution, schema-evolution contract
|
|
40
|
+
- [Flight streaming](data/flight-streaming.md) — cross-process data access via Arrow Flight
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# AI integration
|
|
2
|
+
|
|
3
|
+
The platform exposes its operations as **typed tool calls** an AI client can drive — over an MCP server (the open protocol AI assistants use to call external tools) for clients that speak it, or via the CLI for any agent with a terminal: discovery, capability matching, run launching, results query, config validation, datasheet extraction, test scaffolding. It does not embed an LLM client itself — the user brings their own assistant (Claude Code, Cursor, Cline, Claude Desktop, Copilot CLI, …), and TesterKit is the typed surface the assistant drives.
|
|
4
|
+
|
|
5
|
+
The cost of any structured platform is the upfront encoding work — getting existing instruments and parts encoded as catalog and spec YAML. AI integration is the platform's answer to that cost. An agent reads a datasheet PDF, drafts the catalog YAML, lands it as a file you can diff and review. The structured approach becomes worth adopting because the encoding work shrinks from hours to minutes.
|
|
6
|
+
|
|
7
|
+
The rest of this page documents the integration's boundary: what changes for the user, why it's safe to lean on without losing visibility, what it deliberately doesn't do, and the adoption ramp.
|
|
8
|
+
|
|
9
|
+
## What changes for the user
|
|
10
|
+
|
|
11
|
+
| You're doing | Without AI | With AI |
|
|
12
|
+
|---|---|---|
|
|
13
|
+
| Onboarding a new instrument | Read datasheet, write capability YAML by hand from scratch (hours) | The `testerkit-datasheets` skill produces a reviewable draft; you correct (minutes) |
|
|
14
|
+
| Starting a new part | Read datasheet, write spec YAML, decide instruments, wire station, scaffold test | The `testerkit-datasheets` skill drafts all of it, with approval gates at every phase |
|
|
15
|
+
| Routine test authoring | Look up the spec in the YAML, write the `verify()` calls | (largely unchanged — you stay in pytest) |
|
|
16
|
+
| Run + analyze | (unchanged — pytest runs, results write to parquet) | (unchanged — pytest runs, results write to parquet) |
|
|
17
|
+
|
|
18
|
+
The agent's output is **a starting point you review**, not a finished artifact. Every YAML lands as a file in your repo. Every test is a `def test_*()` you can edit. If the agent gets a capability wrong, you spot it in the diff and push back. If you delete the AI from the loop tomorrow, the project keeps working — the files don't depend on the AI ever running again.
|
|
19
|
+
|
|
20
|
+
## What keeps the integration safe to lean on
|
|
21
|
+
|
|
22
|
+
Three properties of the platform make the AI surface safe to use without losing visibility:
|
|
23
|
+
|
|
24
|
+
1. **Everything is a file.** Parts, stations, fixtures, profiles, sequences, results — all YAML or Parquet. An AI editing a part spec produces a diff you can review the same way you'd review a colleague's PR. There's no opaque database for the AI to mutate.
|
|
25
|
+
|
|
26
|
+
2. **Tool calls, not LLM calls.** TesterKit does not embed an OpenAI / Anthropic / Google client. The AI tooling drives TesterKit from outside — over MCP for clients that speak it, via the CLI for agents with a terminal. You bring your own AI client; TesterKit exposes the operations.
|
|
27
|
+
|
|
28
|
+
3. **Operator-in-the-loop by design.** The `testerkit-datasheets` skill (see [skills reference](../../reference/overview/skills.md)) is a gated, multi-phase pipeline that STOPS at an approval checkpoint after every phase — part spec, instrument match, station wiring, test scaffold. "Here is the part spec I extracted, ok to save?" — you say yes or you edit first.
|
|
29
|
+
|
|
30
|
+
This matters most when the AI gets it wrong. A misread accuracy spec or a wrongly assigned pin reads as a YAML diff or a `verify()` line — you spot it, push back, iterate. Compare to a workflow where the AI commits to a database: the same mistake disappears under "the system says so."
|
|
31
|
+
|
|
32
|
+
## Anti-goals
|
|
33
|
+
|
|
34
|
+
A few things AI integration in TesterKit deliberately does **not** try to do:
|
|
35
|
+
|
|
36
|
+
- **Run the test.** Test execution is pytest. The AI can scaffold a test file, but the test runs the same way it would without the AI.
|
|
37
|
+
- **Decide pass/fail.** Limit checking, traceability, capability matching — all in code. The AI proposes; the deterministic platform decides.
|
|
38
|
+
- **Hide the prompt.** All shipped skills ([skills reference](../../reference/overview/skills.md)) are plain `SKILL.md` markdown files you can read, audit, fork, or ignore.
|
|
39
|
+
- **Lock you to one model.** The `testerkit-datasheets` skill's sub-agent prompts each name a recommended **tier** (e.g. high-capability reasoning for schema-correct YAML emission) but no hard-coded model name. Pick whichever your client supports.
|
|
40
|
+
|
|
41
|
+
## Adoption ramp
|
|
42
|
+
|
|
43
|
+
Three adoption levels — pick whichever matches the user's trust level today:
|
|
44
|
+
|
|
45
|
+
1. **Just tool calls.** Either register the MCP server (`testerkit setup claude-code` and friends) for clients that speak MCP, or have your agent invoke the CLI directly (`testerkit runs`, `testerkit show`, `testerkit discover`, `testerkit metrics`, ...). Drive operations conversationally without any skill matching. "Add a 3.3V output rail to the part spec" — the agent calls `testerkit_project(action="save", ...)` (or edits the YAML file directly and runs `testerkit validate`) and shows the diff.
|
|
46
|
+
|
|
47
|
+
2. **The `testerkit-datasheets` skill as a starting draft.** Point it at a new part's datasheet PDF. Treat the YAML files it produces as a first draft, then hand-edit. Often faster than starting from a blank file.
|
|
48
|
+
|
|
49
|
+
3. **Full datasheet → tests pipeline.** Run the `testerkit-datasheets` skill end-to-end — instrument catalog entries, part spec, station wiring, and test scaffold, in one gated pipeline. Operator approval gates at every phase. See [how-to/datasheet-to-test](../../how-to/catalog/datasheet-to-test.md) for the walkthrough.
|
|
50
|
+
|
|
51
|
+
## See also
|
|
52
|
+
|
|
53
|
+
- [How-to: AI-assisted test development via MCP](../../how-to/overview/mcp-integration.md) — registering the MCP server with each supported AI client
|
|
54
|
+
- [How-to: datasheet-to-test walkthrough](../../how-to/catalog/datasheet-to-test.md) — the `testerkit-datasheets` skill end-to-end
|
|
55
|
+
- [Reference: skills](../../reference/overview/skills.md) — the 11 Agent Skills, MCP tools, and CLI
|
|
56
|
+
- [Reference: MCP server + HTTP API](../../reference/runtime/api.md) — the operations AI clients call over MCP
|
|
57
|
+
- [Reference: CLI](../../reference/cli.md) — the operations AI clients call via the terminal
|