pycontrol-core 3.0.0b2__tar.gz → 3.0.0b4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (345) hide show
  1. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/PKG-INFO +11 -8
  2. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/README.md +10 -7
  3. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/docs/addon-settings.md +33 -26
  4. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/docs/architecture.md +66 -64
  5. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/docs/changelog.md +263 -3
  6. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/docs/cli-reference.md +101 -23
  7. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/docs/contracts.md +46 -11
  8. pycontrol_core-3.0.0b4/docs/core-walkthrough.md +198 -0
  9. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/docs/data-format.md +253 -18
  10. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/docs/experiment-handlers.md +101 -9
  11. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/docs/getting-started.md +23 -11
  12. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/docs/hardware-definitions.md +2 -2
  13. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/docs/maintainability.md +15 -3
  14. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/docs/migration-from-official.md +15 -12
  15. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/docs/public-api.md +208 -40
  16. pycontrol_core-3.0.0b4/docs/release-acceptance.md +186 -0
  17. pycontrol_core-3.0.0b4/docs/sharing.md +171 -0
  18. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/docs/subscriber-api.md +43 -24
  19. pycontrol_core-3.0.0b4/docs/task-folders.md +181 -0
  20. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/docs/task-handlers.md +145 -17
  21. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/docs/testing.md +32 -7
  22. pycontrol_core-3.0.0b4/docs/threading-and-state.md +194 -0
  23. pycontrol_core-3.0.0b4/docs/troubleshooting.md +344 -0
  24. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/docs/wire-protocol.md +68 -12
  25. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/docs/writing-tasks.md +17 -11
  26. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/__init__.py +1 -1
  27. pycontrol_core-3.0.0b4/src/pycontrol/addons/README.md +42 -0
  28. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/addons/__init__.py +26 -18
  29. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/addons/experiment_api.py +37 -4
  30. pycontrol_core-3.0.0b4/src/pycontrol/addons/loader.py +481 -0
  31. pycontrol_core-3.0.0b4/src/pycontrol/addons/resources.py +264 -0
  32. pycontrol_core-3.0.0b4/src/pycontrol/addons/run_context.py +197 -0
  33. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/addons/task_api.py +100 -6
  34. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/analysis/data_import.py +18 -9
  35. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/bundles/README.md +1 -1
  36. pycontrol_core-3.0.0b4/src/pycontrol/bundles/collect.py +127 -0
  37. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/bundles/models.py +74 -22
  38. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/bundles/protocol.py +65 -18
  39. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/bundles/run.py +91 -21
  40. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/bundles/zipio.py +45 -5
  41. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/bus/README.md +9 -2
  42. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/bus/__init__.py +14 -1
  43. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/bus/events.py +1 -1
  44. pycontrol_core-3.0.0b4/src/pycontrol/bus/status.py +241 -0
  45. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/cli/commands/common.py +10 -1
  46. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/cli/commands/experiment_impl.py +1 -1
  47. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/cli/commands/run.py +1 -1
  48. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/cli/commands/run_impl.py +3 -0
  49. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/config/models.py +2 -2
  50. pycontrol_core-3.0.0b4/src/pycontrol/experiment/README.md +39 -0
  51. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/experiment/__init__.py +2 -0
  52. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/experiment/hooks.py +36 -8
  53. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/experiment/models.py +26 -4
  54. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/experiment/planning.py +16 -3
  55. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/experiment/run.py +184 -51
  56. pycontrol_core-3.0.0b4/src/pycontrol/experiment/run_recording.py +214 -0
  57. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/experiment/supervisor.py +70 -42
  58. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/firmware/README.md +15 -2
  59. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/firmware/devices/__init__.py +1 -1
  60. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/firmware/devices/breakout_6port_r0_2_h7.py +2 -5
  61. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/firmware/devices/breakout_6port_r1_2_f4.py +1 -1
  62. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/firmware/pycontrol/framework.py +43 -9
  63. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/firmware/pycontrol/hardware.py +31 -5
  64. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/protocol/decoder.py +1 -6
  65. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/provenance/README.md +3 -2
  66. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/provenance/__init__.py +4 -1
  67. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/provenance/archive.py +33 -17
  68. pycontrol_core-3.0.0b4/src/pycontrol/provenance/collect.py +492 -0
  69. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/provenance/hashing.py +50 -7
  70. pycontrol_core-3.0.0b4/src/pycontrol/provenance/layout.py +131 -0
  71. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/provenance/models.py +38 -3
  72. pycontrol_core-3.0.0b4/src/pycontrol/provenance/source.py +51 -0
  73. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/recording/README.md +4 -2
  74. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/recording/native_event_logger.py +31 -2
  75. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/repl/README.md +6 -3
  76. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/session/README.md +10 -2
  77. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/session/board_session.py +108 -47
  78. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/session/device_resolution.py +1 -1
  79. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/session/protocols.py +16 -1
  80. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/session/pump.py +20 -2
  81. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/transport/README.md +6 -2
  82. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/workspace/README.md +5 -4
  83. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/workspace/__init__.py +196 -42
  84. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/workspace/creation.py +1 -5
  85. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/workspace/hwdef_inspect.py +2 -2
  86. pycontrol_core-3.0.0b4/src/pycontrol/workspace/task_inspect.py +322 -0
  87. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/workspace_template/experiments/blink_demo.json +1 -1
  88. pycontrol_core-3.0.0b4/src/pycontrol/workspace_template/tasks/README.md +65 -0
  89. {pycontrol_core-3.0.0b2/src/pycontrol/workspace_template/tasks/examples → pycontrol_core-3.0.0b4/src/pycontrol/workspace_template/tasks/examples/blinker}/blinker-pattern-h7.py +4 -3
  90. pycontrol_core-3.0.0b4/src/pycontrol/workspace_template/tasks/examples/blinker/blinker_dialog.py +28 -0
  91. pycontrol_core-3.0.0b4/src/pycontrol/workspace_template/tasks/examples/blinker/blinker_status.py +38 -0
  92. {pycontrol_core-3.0.0b2/src/pycontrol/workspace_template/addons/experiment_handlers → pycontrol_core-3.0.0b4/src/pycontrol/workspace_template/tasks/examples/blinker}/notion_logger.py +4 -3
  93. {pycontrol_core-3.0.0b2/src/pycontrol/workspace_template/addons/task_handlers → pycontrol_core-3.0.0b4/src/pycontrol/workspace_template/tasks/examples/blinker}/run_notify.py +3 -2
  94. {pycontrol_core-3.0.0b2/src/pycontrol/workspace_template/addons/experiment_handlers → pycontrol_core-3.0.0b4/src/pycontrol/workspace_template/tasks/examples/blinker}/s3_data_exporter.py +4 -3
  95. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/tests/CLASSIFICATION.md +2 -1
  96. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/tests/integration/test_board_session.py +2 -2
  97. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/tests/integration/test_cli_workspace.py +39 -29
  98. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/tests/integration/test_experiment_runner.py +212 -19
  99. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/tests/integration/test_repl.py +0 -29
  100. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/tests/integration/test_run.py +304 -24
  101. pycontrol_core-3.0.0b4/tests/integration/test_run_resources.py +390 -0
  102. pycontrol_core-3.0.0b4/tests/integration/test_session_failures.py +330 -0
  103. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/tests/unit/firmware/test_hardware_uses_profile.py +4 -10
  104. pycontrol_core-3.0.0b4/tests/unit/firmware/test_run_sequence.py +396 -0
  105. pycontrol_core-3.0.0b4/tests/unit/firmware/test_runtime_variable_commands.py +113 -0
  106. pycontrol_core-3.0.0b4/tests/unit/test_addons.py +850 -0
  107. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/tests/unit/test_bundles_collect.py +118 -72
  108. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/tests/unit/test_cli.py +26 -9
  109. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/tests/unit/test_cli_setups.py +23 -0
  110. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/tests/unit/test_device_resolution.py +1 -2
  111. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/tests/unit/test_device_table_consistency.py +0 -6
  112. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/tests/unit/test_experiment_hook_sequencer.py +8 -4
  113. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/tests/unit/test_experiment_planning.py +25 -2
  114. pycontrol_core-3.0.0b4/tests/unit/test_experiment_run_context.py +263 -0
  115. pycontrol_core-3.0.0b4/tests/unit/test_legacy_run_bundles.py +309 -0
  116. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/tests/unit/test_protocol_decoder.py +30 -7
  117. pycontrol_core-3.0.0b4/tests/unit/test_provenance.py +706 -0
  118. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/tests/unit/test_recording.py +84 -0
  119. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/tests/unit/test_run_bundle_format.py +68 -20
  120. pycontrol_core-3.0.0b4/tests/unit/test_run_bundle_v2.py +576 -0
  121. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/tests/unit/test_run_data_durability.py +23 -13
  122. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/tests/unit/test_run_recovery.py +21 -0
  123. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/tests/unit/test_session_pump.py +6 -5
  124. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/tests/unit/test_session_variables.py +0 -2
  125. pycontrol_core-3.0.0b4/tests/unit/test_status_fields.py +137 -0
  126. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/tests/unit/test_supervisor_join.py +109 -1
  127. pycontrol_core-3.0.0b4/tests/unit/test_task_folders.py +294 -0
  128. pycontrol_core-3.0.0b4/tests/unit/test_task_resources.py +636 -0
  129. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/tests/unit/test_workspace.py +27 -54
  130. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/tests/unit/test_workspace_hwdef_inspect.py +5 -5
  131. pycontrol_core-3.0.0b4/tests/unit/test_workspace_template.py +46 -0
  132. pycontrol_core-3.0.0b2/docs/core-walkthrough.md +0 -109
  133. pycontrol_core-3.0.0b2/docs/release-acceptance.md +0 -115
  134. pycontrol_core-3.0.0b2/docs/sharing.md +0 -145
  135. pycontrol_core-3.0.0b2/docs/threading-and-state.md +0 -227
  136. pycontrol_core-3.0.0b2/docs/troubleshooting.md +0 -257
  137. pycontrol_core-3.0.0b2/src/pycontrol/addons/README.md +0 -40
  138. pycontrol_core-3.0.0b2/src/pycontrol/addons/loader.py +0 -363
  139. pycontrol_core-3.0.0b2/src/pycontrol/bundles/collect.py +0 -265
  140. pycontrol_core-3.0.0b2/src/pycontrol/bus/status.py +0 -106
  141. pycontrol_core-3.0.0b2/src/pycontrol/experiment/README.md +0 -35
  142. pycontrol_core-3.0.0b2/src/pycontrol/experiment/run_recording.py +0 -164
  143. pycontrol_core-3.0.0b2/src/pycontrol/provenance/collect.py +0 -423
  144. pycontrol_core-3.0.0b2/src/pycontrol/provenance/source.py +0 -32
  145. pycontrol_core-3.0.0b2/src/pycontrol/workspace/task_inspect.py +0 -30
  146. pycontrol_core-3.0.0b2/src/pycontrol/workspace_template/addons/controls/README.md +0 -113
  147. pycontrol_core-3.0.0b2/src/pycontrol/workspace_template/addons/controls/example_dialog.py +0 -20
  148. pycontrol_core-3.0.0b2/src/pycontrol/workspace_template/addons/experiment_handlers/README.md +0 -94
  149. pycontrol_core-3.0.0b2/src/pycontrol/workspace_template/addons/plotters/README.md +0 -103
  150. pycontrol_core-3.0.0b2/src/pycontrol/workspace_template/addons/task_handlers/README.md +0 -86
  151. pycontrol_core-3.0.0b2/src/pycontrol/workspace_template/addons/task_handlers/example_handler.py +0 -32
  152. pycontrol_core-3.0.0b2/src/pycontrol/workspace_template/addons/task_handlers/trial_summary.py +0 -31
  153. pycontrol_core-3.0.0b2/tests/unit/firmware/test_run_sequence.py +0 -84
  154. pycontrol_core-3.0.0b2/tests/unit/test_addons.py +0 -620
  155. pycontrol_core-3.0.0b2/tests/unit/test_provenance.py +0 -348
  156. pycontrol_core-3.0.0b2/tests/unit/test_status_fields.py +0 -73
  157. pycontrol_core-3.0.0b2/tests/unit/test_workspace_template.py +0 -36
  158. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/.complexipy_cache/.gitignore +0 -0
  159. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/.complexipy_cache/CACHEDIR.TAG +0 -0
  160. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/.complexipy_cache/README.md +0 -0
  161. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/.complexipy_cache/v/cache/functions +0 -0
  162. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/.gitignore +0 -0
  163. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/.pre-commit-config.yaml +0 -0
  164. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/.python-version +0 -0
  165. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/CONTRIBUTING.md +0 -0
  166. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/LICENSE.txt +0 -0
  167. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/docs/design-history/simplified-pycontrol-provenance-and-sharing-motivation.md +0 -0
  168. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/docs/design-history/simplified-pycontrol-provenance-and-sharing-plan.md +0 -0
  169. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/pyproject.toml +0 -0
  170. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/addons/controls_context.py +0 -0
  171. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/analysis/README.md +0 -0
  172. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/analysis/__init__.py +0 -0
  173. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/bundles/__init__.py +0 -0
  174. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/bus/subscriber.py +0 -0
  175. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/cli/README.md +0 -0
  176. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/cli/__init__.py +0 -0
  177. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/cli/commands/__init__.py +0 -0
  178. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/cli/commands/board.py +0 -0
  179. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/cli/commands/board_impl.py +0 -0
  180. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/cli/commands/data.py +0 -0
  181. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/cli/commands/data_impl.py +0 -0
  182. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/cli/commands/experiment.py +0 -0
  183. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/cli/commands/firmware.py +0 -0
  184. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/cli/commands/firmware_impl.py +0 -0
  185. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/cli/commands/gui.py +0 -0
  186. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/cli/commands/gui_impl.py +0 -0
  187. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/cli/commands/setups.py +0 -0
  188. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/cli/commands/setups_impl.py +0 -0
  189. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/cli/commands/vars.py +0 -0
  190. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/cli/commands/vars_impl.py +0 -0
  191. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/cli/commands/workspace.py +0 -0
  192. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/cli/commands/workspace_impl.py +0 -0
  193. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/cli/main.py +0 -0
  194. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/config/README.md +0 -0
  195. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/config/__init__.py +0 -0
  196. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/config/io.py +0 -0
  197. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/firmware/__init__.py +0 -0
  198. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/firmware/device_helpers.py +0 -0
  199. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/firmware/devices/LED_driver.py +0 -0
  200. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/firmware/devices/MCP.py +0 -0
  201. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/firmware/devices/VL53L4.py +0 -0
  202. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/firmware/devices/analog_LED.py +0 -0
  203. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/firmware/devices/audio_board.py +0 -0
  204. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/firmware/devices/audio_player.py +0 -0
  205. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/firmware/devices/breakout_10port_r0_4_h7.py +0 -0
  206. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/firmware/devices/breakout_10port_r0_8_h7.py +0 -0
  207. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/firmware/devices/breakout_6port_r0_1_h7.py +0 -0
  208. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/firmware/devices/five_poke.py +0 -0
  209. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/firmware/devices/frame_logger.py +0 -0
  210. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/firmware/devices/frame_trigger.py +0 -0
  211. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/firmware/devices/grid_maze.py +0 -0
  212. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/firmware/devices/lickometer.py +0 -0
  213. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/firmware/devices/load_cell.py +0 -0
  214. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/firmware/devices/nine_poke.py +0 -0
  215. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/firmware/devices/poke.py +0 -0
  216. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/firmware/devices/port_expander.py +0 -0
  217. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/firmware/devices/rotary_encoder.py +0 -0
  218. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/firmware/devices/schmitt_trigger.py +0 -0
  219. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/firmware/devices/solenoid_driver.py +0 -0
  220. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/firmware/devices/stepper_motor.py +0 -0
  221. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/firmware/devices/uRFID.py +0 -0
  222. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/firmware/devices/uart_handler.py +0 -0
  223. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/firmware/pycontrol/__init__.py +0 -0
  224. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/firmware/pycontrol/audio.py +0 -0
  225. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/firmware/pycontrol/profiles.py +0 -0
  226. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/firmware/pycontrol/state_machine.py +0 -0
  227. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/firmware/pycontrol/timer.py +0 -0
  228. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/firmware/pycontrol/utility.py +0 -0
  229. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/fsutil.py +0 -0
  230. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/integrations/README.md +0 -0
  231. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/integrations/__init__.py +0 -0
  232. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/integrations/_queue.py +0 -0
  233. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/integrations/notify/__init__.py +0 -0
  234. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/integrations/notify/slack.py +0 -0
  235. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/integrations/notify/telegram.py +0 -0
  236. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/mcu.py +0 -0
  237. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/protocol/README.md +0 -0
  238. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/protocol/__init__.py +0 -0
  239. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/protocol/analog.py +0 -0
  240. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/protocol/encoder.py +0 -0
  241. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/protocol/messages.py +0 -0
  242. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/recording/__init__.py +0 -0
  243. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/recording/analog_writer.py +0 -0
  244. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/recording/console.py +0 -0
  245. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/recording/event_rows.py +0 -0
  246. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/repl/__init__.py +0 -0
  247. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/repl/device_helpers.py +0 -0
  248. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/repl/file_transfer.py +0 -0
  249. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/repl/json_repl.py +0 -0
  250. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/repl/raw_repl.py +0 -0
  251. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/session/__init__.py +0 -0
  252. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/session/info.py +0 -0
  253. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/session/link.py +0 -0
  254. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/session/phases.py +0 -0
  255. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/session/rows.py +0 -0
  256. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/session/services/__init__.py +0 -0
  257. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/session/services/firmware.py +0 -0
  258. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/session/services/maintenance.py +0 -0
  259. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/session/services/setup.py +0 -0
  260. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/session/services/sync.py +0 -0
  261. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/session/types.py +0 -0
  262. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/session/variables.py +0 -0
  263. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/testing/__init__.py +0 -0
  264. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/testing/fake_board.py +0 -0
  265. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/testing/frames.py +0 -0
  266. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/transport/__init__.py +0 -0
  267. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/transport/base.py +0 -0
  268. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/transport/mock.py +0 -0
  269. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/transport/replay.py +0 -0
  270. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/transport/serial.py +0 -0
  271. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/workspace_template/__init__.py +0 -0
  272. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/workspace_template/devices/__init__.py +0 -0
  273. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/workspace_template/devices/example_device.py +0 -0
  274. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/workspace_template/settings.json +0 -0
  275. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/src/pycontrol/workspace_template/setups.json +0 -0
  276. {pycontrol_core-3.0.0b2/src/pycontrol/workspace_template/addons/controls → pycontrol_core-3.0.0b4/src/pycontrol/workspace_template/tasks/examples/blinker}/blinker_gui.json +0 -0
  277. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/tests/__init__.py +0 -0
  278. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/tests/contracts/test_wire_contract.py +0 -0
  279. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/tests/firmware_stubs/__init__.py +0 -0
  280. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/tests/firmware_stubs/machine.py +0 -0
  281. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/tests/firmware_stubs/micropython.py +0 -0
  282. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/tests/firmware_stubs/pyb.py +0 -0
  283. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/tests/firmware_stubs/ucollections.py +0 -0
  284. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/tests/firmware_stubs/ujson.py +0 -0
  285. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/tests/golden/__init__.py +0 -0
  286. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/tests/golden/frames/blinker_real.in.bin +0 -0
  287. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/tests/golden/frames/blinker_real.out.bin +0 -0
  288. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/tests/golden/frames/counter_demo_real.in.bin +0 -0
  289. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/tests/golden/frames/counter_demo_real.out.bin +0 -0
  290. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/tests/golden/frames/encoder_real.in.bin +0 -0
  291. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/tests/golden/frames/encoder_real.out.bin +0 -0
  292. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/tests/golden/replay.py +0 -0
  293. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/tests/golden/sessions/blinker_real.meta.json +0 -0
  294. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/tests/golden/sessions/blinker_real.tsv +0 -0
  295. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/tests/golden/sessions/counter_demo_real.meta.json +0 -0
  296. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/tests/golden/sessions/counter_demo_real.tsv +0 -0
  297. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/tests/golden/sessions/encoder_real.meta.json +0 -0
  298. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/tests/golden/sessions/encoder_real.tsv +0 -0
  299. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/tests/golden/sessions/encoder_real_encoder.data.npy +0 -0
  300. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/tests/golden/sessions/encoder_real_encoder.time.npy +0 -0
  301. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/tests/golden/tasks/blinker_real.task.py +0 -0
  302. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/tests/golden/tasks/counter_demo_real.task.py +0 -0
  303. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/tests/golden/tasks/encoder_real.task.py +0 -0
  304. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/tests/integration/__init__.py +0 -0
  305. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/tests/integration/test_real_hardware_replay.py +0 -0
  306. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/tests/unit/__init__.py +0 -0
  307. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/tests/unit/firmware/__init__.py +0 -0
  308. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/tests/unit/firmware/conftest.py +0 -0
  309. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/tests/unit/firmware/test_analog_trigger_variables.py +0 -0
  310. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/tests/unit/firmware/test_audio.py +0 -0
  311. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/tests/unit/firmware/test_breakout_qwiic.py +0 -0
  312. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/tests/unit/firmware/test_device_helpers.py +0 -0
  313. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/tests/unit/firmware/test_devices_init.py +0 -0
  314. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/tests/unit/firmware/test_profiles.py +0 -0
  315. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/tests/unit/firmware/test_rotary_encoder.py +0 -0
  316. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/tests/unit/firmware/test_schmitt_trigger_threshold.py +0 -0
  317. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/tests/unit/firmware/test_state_machine_variables.py +0 -0
  318. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/tests/unit/firmware/test_timer_rounding.py +0 -0
  319. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/tests/unit/firmware/test_utility_log.py +0 -0
  320. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/tests/unit/firmware/test_vl53l4.py +0 -0
  321. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/tests/unit/test_analysis_data_import.py +0 -0
  322. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/tests/unit/test_board_link.py +0 -0
  323. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/tests/unit/test_bus_subscriber.py +0 -0
  324. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/tests/unit/test_config_models.py +0 -0
  325. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/tests/unit/test_file_transfer_errors.py +0 -0
  326. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/tests/unit/test_firmware_root.py +0 -0
  327. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/tests/unit/test_firmware_service.py +0 -0
  328. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/tests/unit/test_firmware_status.py +0 -0
  329. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/tests/unit/test_integrations_notify.py +0 -0
  330. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/tests/unit/test_maintenance_service.py +0 -0
  331. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/tests/unit/test_phases.py +0 -0
  332. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/tests/unit/test_protocol_encoder.py +0 -0
  333. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/tests/unit/test_public_api.py +0 -0
  334. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/tests/unit/test_raw_repl.py +0 -0
  335. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/tests/unit/test_serial_transport.py +0 -0
  336. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/tests/unit/test_session_rows.py +0 -0
  337. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/tests/unit/test_setup_identity.py +0 -0
  338. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/tests/unit/test_subject_id.py +0 -0
  339. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/tests/unit/test_transport_mock.py +0 -0
  340. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/tools/README.md +0 -0
  341. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/tools/capture_fixtures.py +0 -0
  342. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/tools/hardware_acceptance.py +0 -0
  343. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/tools/maintainability_metrics.py +0 -0
  344. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/tools/run_tests.py +0 -0
  345. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b4}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: pycontrol-core
3
- Version: 3.0.0b2
3
+ Version: 3.0.0b4
4
4
  Summary: Sans-IO host library for the pyControl behavioural-experiment framework.
5
5
  Project-URL: Homepage, https://github.com/pyControl/core
6
6
  Author: pyControl contributors
@@ -91,11 +91,15 @@ uv sync --extra test --extra lint
91
91
  Run checks:
92
92
 
93
93
  ```bash
94
- uv run pytest
94
+ uv run python tools/run_tests.py -q
95
95
  uv run ruff check src tests
96
+ uv run ruff format --check src tests
96
97
  uv run mypy
97
98
  ```
98
99
 
100
+ The test runner enforces a process deadline and reports thread traces if a test
101
+ hangs. See [Testing](docs/testing.md) for focused checks and hardware limits.
102
+
99
103
  ## Create A Workspace
100
104
 
101
105
  Most lab-authored files live in a workspace, not inside this package.
@@ -110,13 +114,9 @@ The workspace contains:
110
114
 
111
115
  | Path | Purpose |
112
116
  | --- | --- |
113
- | `tasks/` | Task files uploaded to the board. |
117
+ | `tasks/` | Task files uploaded to the board, each with its host-side addons (task and experiment handlers, GUI plotters and controls) in its task folder ([task-folders.md](docs/task-folders.md)). |
114
118
  | `hardware_definitions/` | Rig wiring files uploaded as `hardware_definition.py`. |
115
119
  | `devices/` | Workspace device classes uploaded when referenced. |
116
- | `addons/task_handlers/` | Host-side task automation. |
117
- | `addons/experiment_handlers/` | Whole-experiment automation. |
118
- | `addons/plotters/` | Optional GUI plotters. |
119
- | `addons/controls/` | Optional GUI control panels. |
120
120
  | `experiments/*.json` | Multi-subject experiment definitions. |
121
121
  | `settings.json` / `setups.json` | Workspace settings and setup registry. |
122
122
  | `data/` | Default output folder for native `.pycontrol.zip` run bundles. |
@@ -126,7 +126,7 @@ For a full walkthrough, use [getting-started.md](docs/getting-started.md).
126
126
  ## Run A Task
127
127
 
128
128
  ```bash
129
- pycontrol run --setup BoxA --task examples/blinker \
129
+ pycontrol run --setup BoxA --task examples/blinker/blinker-pattern-h7 \
130
130
  --subject mouse42 --duration 10
131
131
  ```
132
132
 
@@ -197,6 +197,9 @@ See [public-api.md](docs/public-api.md) for the supported import surface.
197
197
  Start with [Understanding one run](docs/core-walkthrough.md), then use the
198
198
  references below. [Release acceptance](docs/release-acceptance.md) distinguishes
199
199
  host verification from the hardware evidence required for lab deployment.
200
+ Ownership and completion rules are detailed in
201
+ [Threading and state](docs/threading-and-state.md). The tradeoffs to
202
+ preserve are recorded in [Maintainability decisions](docs/maintainability.md).
200
203
 
201
204
  Orientation and operation:
202
205
 
@@ -49,11 +49,15 @@ uv sync --extra test --extra lint
49
49
  Run checks:
50
50
 
51
51
  ```bash
52
- uv run pytest
52
+ uv run python tools/run_tests.py -q
53
53
  uv run ruff check src tests
54
+ uv run ruff format --check src tests
54
55
  uv run mypy
55
56
  ```
56
57
 
58
+ The test runner enforces a process deadline and reports thread traces if a test
59
+ hangs. See [Testing](docs/testing.md) for focused checks and hardware limits.
60
+
57
61
  ## Create A Workspace
58
62
 
59
63
  Most lab-authored files live in a workspace, not inside this package.
@@ -68,13 +72,9 @@ The workspace contains:
68
72
 
69
73
  | Path | Purpose |
70
74
  | --- | --- |
71
- | `tasks/` | Task files uploaded to the board. |
75
+ | `tasks/` | Task files uploaded to the board, each with its host-side addons (task and experiment handlers, GUI plotters and controls) in its task folder ([task-folders.md](docs/task-folders.md)). |
72
76
  | `hardware_definitions/` | Rig wiring files uploaded as `hardware_definition.py`. |
73
77
  | `devices/` | Workspace device classes uploaded when referenced. |
74
- | `addons/task_handlers/` | Host-side task automation. |
75
- | `addons/experiment_handlers/` | Whole-experiment automation. |
76
- | `addons/plotters/` | Optional GUI plotters. |
77
- | `addons/controls/` | Optional GUI control panels. |
78
78
  | `experiments/*.json` | Multi-subject experiment definitions. |
79
79
  | `settings.json` / `setups.json` | Workspace settings and setup registry. |
80
80
  | `data/` | Default output folder for native `.pycontrol.zip` run bundles. |
@@ -84,7 +84,7 @@ For a full walkthrough, use [getting-started.md](docs/getting-started.md).
84
84
  ## Run A Task
85
85
 
86
86
  ```bash
87
- pycontrol run --setup BoxA --task examples/blinker \
87
+ pycontrol run --setup BoxA --task examples/blinker/blinker-pattern-h7 \
88
88
  --subject mouse42 --duration 10
89
89
  ```
90
90
 
@@ -155,6 +155,9 @@ See [public-api.md](docs/public-api.md) for the supported import surface.
155
155
  Start with [Understanding one run](docs/core-walkthrough.md), then use the
156
156
  references below. [Release acceptance](docs/release-acceptance.md) distinguishes
157
157
  host verification from the hardware evidence required for lab deployment.
158
+ Ownership and completion rules are detailed in
159
+ [Threading and state](docs/threading-and-state.md). The tradeoffs to
160
+ preserve are recorded in [Maintainability decisions](docs/maintainability.md).
158
161
 
159
162
  Orientation and operation:
160
163
 
@@ -6,20 +6,22 @@ Use addon settings for values that should not live in a task file or experiment
6
6
 
7
7
  ## Supported Addon Types
8
8
 
9
- Settings can be declared by:
9
+ Settings can be declared by the task handlers, experiment handlers and task
10
+ plotters in a [task folder](task-folders.md).
10
11
 
11
- - task handlers in `addons/task_handlers/*.py`
12
- - experiment handlers in `addons/experiment_handlers/*.py`
13
- - task plotters in `addons/plotters/*.py`
14
-
15
- The settings are stored separately by addon type and file stem:
12
+ The settings are stored in the workspace `settings.json`, never in the task
13
+ folder, separately by addon type and keyed by the addon's path relative to
14
+ `tasks/` (without `.py`), so same-named addons in two folders keep separate
15
+ settings:
16
16
 
17
17
  ```python
18
- settings.addons.task_handlers["run_notify"]
19
- settings.addons.experiment_handlers["training_summary"]
20
- settings.addons.plotters["SequencePlotter"]
18
+ settings.addons.task_handlers["sequence/run_notify"]
19
+ settings.addons.experiment_handlers["training/training_summary"]
20
+ settings.addons.plotters["sequence/sequence_plotter"]
21
21
  ```
22
22
 
23
+ The GUI's built-in plot keeps the key `settings.addons.plotters["DefaultPlotter"]`.
24
+
23
25
  ## Declaring Settings
24
26
 
25
27
  Add a `SETTINGS` tuple to the addon module. Each entry should be made with `BoolSetting`, `IntSetting`, or `StringSetting`.
@@ -83,6 +85,12 @@ StringSetting("bot_token", "Bot token", secret=True)
83
85
 
84
86
  Secret fields are masked in the Settings dialog, but they are still saved in the workspace `settings.json`.
85
87
 
88
+ A run bundle records the settings each task handler and experiment handler
89
+ was configured with when it loaded, minus the secret ones, whose names it
90
+ lists under `redacted_settings`. Which names are secret is read from the
91
+ `SETTINGS` of the loaded code (module level, or on a class the module
92
+ defines); when that cannot be read, every value of the addon is redacted.
93
+
86
94
  All setting helpers accept `hint="..."`. When provided, the Settings dialog shows
87
95
  the hint as a tooltip when the user hovers over the setting label.
88
96
 
@@ -91,14 +99,14 @@ the hint as a tooltip when the user hovers over the setting label.
91
99
  At runtime, the addon receives two attributes:
92
100
 
93
101
  ```python
94
- self.addon_settings # dict for this addon file stem
102
+ self.addon_settings # dict for this addon, keyed by its path relative to tasks/
95
103
  self.workspace_settings # full workspace Settings object
96
104
  ```
97
105
 
98
- For a task handler file named `addons/task_handlers/run_notify.py`, settings are passed from:
106
+ For a task handler file `tasks/sequence/run_notify.py`, settings are passed from:
99
107
 
100
108
  ```python
101
- self.workspace_settings.addons.task_handlers["run_notify"]
109
+ self.workspace_settings.addons.task_handlers["sequence/run_notify"]
102
110
  ```
103
111
 
104
112
  and are usually read through:
@@ -112,14 +120,14 @@ enabled = self.addon_settings.get("slack_enabled", False)
112
120
  Task-addon settings are configured for `pycontrol run`, `pycontrol
113
121
  experiment`, and GUI-run sessions before callbacks run.
114
122
 
115
- For an experiment handler file named `addons/experiment_handlers/training_summary.py`, settings are passed from:
123
+ For an experiment handler file `tasks/training/training_summary.py`, settings are passed from:
116
124
 
117
125
  ```python
118
- self.workspace_settings.addons.experiment_handlers["training_summary"]
126
+ self.workspace_settings.addons.experiment_handlers["training/training_summary"]
119
127
  ```
120
128
 
121
129
  Task plotters use the same stored settings through
122
- `settings.addons.plotters[stem]`. In the GUI, `PlotterHost` passes that
130
+ `settings.addons.plotters["<folder>/<stem>"]`. In the GUI, `PlotterHost` passes that
123
131
  dict to custom plotter constructors as `addon_settings` when the constructor
124
132
  accepts that keyword argument.
125
133
 
@@ -169,7 +177,7 @@ class RunNotify(TaskHandler):
169
177
  self._notify.close()
170
178
  ```
171
179
 
172
- Select this addon by either naming it after the task, or adding this to the task file:
180
+ Put the file in the task's folder and select it in the task file:
173
181
 
174
182
  ```python
175
183
  v.handler = "run_notify"
@@ -197,19 +205,18 @@ class TelegramExperimentNotify(ExperimentHandler):
197
205
  print(f"Would notify {chat_id} that {context.experiment_name} finished.")
198
206
  ```
199
207
 
200
- Select this addon with the experiment setup `Experiment handler` picker.
208
+ Put the file in the folder of the experiment's task and select it with the
209
+ experiment setup `Experiment handler` picker.
201
210
 
202
211
  ## Settings Dialog Discovery
203
212
 
204
- The Settings dialog scans addon folders when it opens:
205
-
206
- ```text
207
- addons/task_handlers/*.py
208
- addons/experiment_handlers/*.py
209
- addons/plotters/*.py
210
- ```
211
-
212
- Files whose names start with `_` are skipped. Files with no valid `SETTINGS` are still valid addons, but they do not get a Settings dialog group.
213
+ When it opens, the Settings dialog lists the task handlers, experiment
214
+ handlers and plotters of every task folder. They are found statically, by a
215
+ class deriving from `TaskHandler`, `ExperimentHandler` or `TaskPlotter`
216
+ (`pycontrol.workspace.classify_task_folder`), then imported to read
217
+ `SETTINGS`. Files whose names start with `_` are helpers and are skipped.
218
+ Files with no valid `SETTINGS` are still valid addons, but they do not get a
219
+ Settings dialog group.
213
220
 
214
221
  Each configurable file page has a `Use defaults` button. It resets only the
215
222
  currently selected addon or plotter settings group to the defaults declared
@@ -2,12 +2,12 @@
2
2
 
3
3
  This document describes the implemented `pycontrol-core` architecture. It is the
4
4
  engine shared by the CLI, `pycontrol-gui`, tests, and downstream Python tools.
5
- For frozen compatibility contracts, see `contracts.md`, `wire-protocol.md`,
6
- `data-format.md`, and `threading-and-state.md`. For a guided, layered
7
- learning path through this architecture — from a one-paragraph mental model
8
- down to per-subsystem deep dives — see [core-walkthrough.md](core-walkthrough.md).
5
+ Start with [core-walkthrough.md](core-walkthrough.md) for the reading order,
6
+ ownership, and normal and failed runs. This page is the
7
+ package map. [contracts.md](contracts.md) and [public-api.md](public-api.md)
8
+ distinguish frozen compatibility contracts from provisional frontend APIs.
9
9
 
10
- The shortest mental model is:
10
+ The acquisition path is:
11
11
 
12
12
  ```text
13
13
  Pyboard <-> transport bytes <-> sans-IO protocol <-> BoardSession
@@ -35,23 +35,27 @@ they are not installed into `pycontrol-core`.
35
35
  flowchart TD
36
36
  Frontends["Frontends: pycontrol CLI, pycontrol-gui, Python APIs"]
37
37
  Workspace["workspace/ + config/: discover tasks, setups, experiments, settings"]
38
- Session["session/ BoardSession: FSM, REPL setup, run commands"]
38
+ Run["experiment/ Run: prepare, start, finish"]
39
+ Session["session/ BoardSession: lifecycle, REPL setup, run commands"]
39
40
  Pump["session/pump.py SessionPump: read run bytes"]
40
41
  Bus["bus/ SubscriberRegistry + typed events"]
41
42
  Protocol["protocol/: sans-IO decoder/encoder"]
42
- Transport["transport/: Serial, Mock, Replay, Recording"]
43
+ Transport["transport/: Serial, Mock, Replay"]
43
44
  Board["Pyboard + vendored MicroPython framework"]
44
45
  Recording["recording/: NativeEventLogger, analog writer, console"]
45
46
  Provenance["provenance/: collect, hash, stage, finalize"]
46
47
  Bundles["bundles/: run and protocol ZIP formats"]
47
- Experiment["experiment/: Supervisor, hooks"]
48
+ Experiment["experiment/: Supervisor + experiment hooks"]
48
49
  Addons["addons/: task/experiment APIs, controls specs, loaders"]
49
50
  Analysis["analysis/: read run bundles into Python data structures"]
50
51
  Firmware["firmware/: on-board framework, devices, profiles"]
51
52
 
52
53
  Frontends --> Workspace
53
- Frontends --> Session
54
- Workspace --> Session
54
+ Frontends --> Run
55
+ Frontends --> Experiment
56
+ Experiment --> Run
57
+ Workspace --> Run
58
+ Run --> Session
55
59
  Session --> Transport
56
60
  Transport <--> Board
57
61
  Transport --> Protocol
@@ -61,8 +65,8 @@ flowchart TD
61
65
  Session --> Bus
62
66
  Bus --> Recording
63
67
  Bus --> Addons
64
- Bus --> Experiment
65
- Recording --> Provenance
68
+ Run --> Provenance
69
+ Recording --> Bundles
66
70
  Provenance --> Bundles
67
71
  Bundles --> Analysis
68
72
  Firmware -. synced to .-> Board
@@ -79,9 +83,9 @@ flowchart TD
79
83
  | `provenance/` | File identity, hashing, run staging, protocol references, and run-bundle finalization. |
80
84
  | `bundles/` | Run bundle and protocol bundle ZIP readers/writers, manifests, and validation. |
81
85
  | `analysis/` | Post-run helpers for reading bundle event tables and session dataframes. |
82
- | `experiment/` | Headless multi-rig `Supervisor`, rig outcomes, and experiment hooks. |
86
+ | `experiment/` | Shared single-run `Run`/`RunPlan`, task loading and connection helpers, headless `Supervisor`, rig outcomes and experiment hooks. |
83
87
  | `addons/` | Task handlers, experiment handlers, task-controls specs, addon loaders, and settings collection. |
84
- | `workspace/` | Workspace discovery, path resolution, data-directory resolution, addon lookup, and hwdef inspection. |
88
+ | `workspace/` | Workspace discovery, path resolution, data-directory resolution, task detection, task folders and addon lookup, and hwdef inspection. |
85
89
  | `config/` | Pydantic models and load/save helpers for `settings.json`, `setups.json`, and experiments. |
86
90
  | `firmware/` | Vendored on-board pyControl framework, board profiles, breakouts, and device drivers. |
87
91
  | `integrations/` | Optional notification/storage primitives used by trusted workspace addons. |
@@ -97,8 +101,8 @@ services (`FirmwareService`, `StateMachineSetupService`,
97
101
  the stored results (`identity`, `firmware`, `device_sync`, `last_erase`).
98
102
  Services never reach back into the session; the one upward call is the link's
99
103
  `on_repl_reset` hook, which tells the session to forget its loaded task after a
100
- recovery soft-reset. The stable surface is the documented public API and
101
- contracts, not the internal file layout.
104
+ recovery soft-reset. The compatibility tier of each documented surface is listed in
105
+ [public-api.md](public-api.md); internal file layout is not a contract.
102
106
 
103
107
  ## Single-Run Pipeline
104
108
 
@@ -140,8 +144,9 @@ sequenceDiagram
140
144
  Run-->>Driver: RunResult(bundle_path | staging_dir + error)
141
145
  ```
142
146
 
143
- `Run.finish()` is the only code that turns a recording into a bundle and it
144
- never fails silently: a missing bundle is an ERROR log, a
147
+ `Run.finish()` is the shared driver entry point that closes and packages a
148
+ recording; lower-level bundle/recovery helpers implement those operations.
149
+ It never reports a packaging failure as success: a missing bundle is an ERROR log, a
145
150
  `RunObserver.on_data_at_risk(staging_dir, error)` call, and a non-`ok`
146
151
  `RunResult` naming the staging directory. Drivers map that onto their own
147
152
  surface (CLI exit 1 + stderr, `RigOutcome.error`, GUI `error` signal).
@@ -150,6 +155,10 @@ The recorder opens explicitly before framework startup. It stays open through
150
155
  task-handler end hooks; `Run.finish()` closes it after confirmed completion.
151
156
  A recorder error is sticky and makes the result fail even when cleanup succeeds.
152
157
  Task/transport failures also produce failed results when partial data is saved.
158
+ An uncertain startup retains its original error and attempted-start metadata,
159
+ then keeps staging instead of packaging it. A completion timeout leaves the
160
+ recorder and resource store owned by `Run` for a later retry. The GUI establishes
161
+ that ownership before calling `Run.start()`.
153
162
 
154
163
  The default completed artifact is a `.pycontrol.zip` native run bundle. During a
155
164
  run, `NativeEventLogger` writes the native event table and analog payloads into a
@@ -175,9 +184,18 @@ Experiment-addon hooks are sequenced by `ExperimentHookSequencer`
175
184
  `experiment_cancelled` and builds every `ExperimentHookContext` from a
176
185
  `{subject_id: RigOutcome}` mapping. `Supervisor` and the GUI's experiment run
177
186
  view both drive the same sequencer (the GUI's `SubjectOutcome` *is* a
178
- `RigOutcome`), so the two drivers cannot drift on hook order or context shape.
187
+ `RigOutcome`), so both drivers share the once-only hooks and context construction.
179
188
  *When* a rig counts as started or finished stays with each driver.
180
189
 
190
+ Each experiment run (a GUI run of an experiment, a `Supervisor.run()` call)
191
+ has one fresh experiment handler instance and one `ExperimentRunContext`
192
+ (`addons/run_context.py`), made by `ExperimentRunContext.create` before any
193
+ rig is prepared: a unique `experiment_run_id` plus the frozen JSON
194
+ `shared_data` the handler returns from `shared_run_data()`. The same object
195
+ goes into every rig's `RunPlan.experiment_run` (bound to the task handler by
196
+ `Run.start()` before `prepare_run`) and every hook context, so rigs never wait
197
+ for experiment callbacks to learn which run they belong to.
198
+
181
199
  ```mermaid
182
200
  flowchart LR
183
201
  Exp["Experiment config"] --> Supervisor["Supervisor"]
@@ -194,7 +212,11 @@ flowchart LR
194
212
  driver calls it directly any more.
195
213
 
196
214
  Each rig records independently. Cross-rig coordination is experiment-hook
197
- sequencing, not a merged event stream.
215
+ sequencing, not a merged event stream. Duplicate setup assignments are rejected
216
+ before sessions open. The Supervisor uses a finite join grace after its duration
217
+ or stop condition; slow preparation/finalization can consume that grace. A rig
218
+ reported as timed out may still have a daemon thread alive when the CLI exits.
219
+ The workload/timeout decision remains a release gate.
198
220
 
199
221
  ## Protocol, Transport, And REPL
200
222
 
@@ -218,33 +240,20 @@ fast commands such as `set_variable`, `get_variable`, `trigger_event`, and
218
240
 
219
241
  ## BoardSession Lifecycle
220
242
 
221
- `BoardSession` is the only mutable owner of a live board connection. Each
222
- instance should have one command-thread owner at a time. During a run it also
223
- starts a pump thread that reads bytes and dispatches bus events.
224
-
225
- ```text
226
- DISCONNECTED -> CONNECTING -> CONNECTED -> READY -> RUNNING -> STOPPED
227
- any non-closed state -> CLOSED
228
- ```
229
-
230
- Valid transitions:
243
+ `BoardSession` alone owns lifecycle state. A normal run follows
244
+ `DISCONNECTED → CONNECTING → CONNECTED → READY → RUNNING → STOPPED`.
245
+ A new task setup moves STOPPED back to READY. The command owner confirms pump
246
+ completion and join before STOPPED; receiving an end marker or bus notification
247
+ alone does not make that transition.
231
248
 
232
- | From | Operation | To |
233
- | --- | --- | --- |
234
- | `DISCONNECTED` | `connect()` begins | `CONNECTING` |
235
- | `CONNECTING` | `connect()` succeeds | `CONNECTED` |
236
- | `CONNECTING` | `connect()` fails | `DISCONNECTED` |
237
- | `CONNECTED` | `setup_state_machine()` | `READY` |
238
- | `STOPPED` | `setup_state_machine()` | `READY` |
239
- | `READY` | `start()` | `RUNNING` |
240
- | `RUNNING` | `stop()` or board end-of-run marker | `STOPPED` |
241
- | any non-closed state | `close()` | `CLOSED` |
249
+ Uncertain startup or reader failure makes the session unusable: request stop
250
+ best-effort, complete callbacks, and invalidate to CLOSED. `close()` also reaches
251
+ CLOSED, but raises and remains retryable while callbacks are alive. After
252
+ callbacks finish, transport-disposal errors are logged once and disposal is
253
+ terminal. CLOSED is no proof of physical board stop or OS handle release.
242
254
 
243
- Invalid transitions raise `BoardSessionError`. Hardware-definition upload does
244
- not create a state; it syncs board files used by task setup.
245
-
246
- See `threading-and-state.md` for the authoritative threading and transition
247
- contract.
255
+ Use [threading-and-state.md](threading-and-state.md) for the authoritative state
256
+ transitions, callback contexts and shutdown barriers.
248
257
 
249
258
  ## Event Bus And Subscribers
250
259
 
@@ -283,7 +292,8 @@ The bundle contains:
283
292
  - `meta.json` with run identity, board identity, software versions, variables,
284
293
  setup/experiment metadata, and provenance references.
285
294
  - Native `events.tsv` with typed rows for info, state, event, variable,
286
- threshold, print (including operator notes), log, status, warning, and error.
295
+ threshold, print (including operator notes), log, status, warning, error and
296
+ runtime resource capture.
287
297
  - Analog payloads.
288
298
  - Snapshots and hashes for task, hardware definition, devices, addons, and
289
299
  relevant workspace settings.
@@ -381,26 +391,18 @@ addon loading around those core surfaces. A future web or service frontend
381
391
  should be able to use the same core surfaces without changing protocol,
382
392
  transport, session, or recording code.
383
393
 
384
- ## Stability
385
-
386
- Stable contracts:
387
-
388
- - Wire protocol and deployed firmware frame semantics.
389
- - Session state names and valid lifecycle transitions.
390
- - Bus event dataclass names and callback names.
391
- - Native run bundle layout.
392
- - Documented public import surfaces.
393
-
394
- Implementation details:
394
+ ## Compatibility and release readiness
395
395
 
396
- - Internal package/file layout under `session/services/`, `cli/commands/`, and
397
- provenance helpers.
398
- - Exact lock scopes inside `BoardSession`.
399
- - CLI command parser internals and GUI orchestration.
400
- - Test fixtures and private helper classes.
396
+ The beta freezes addon-author APIs, bus events/callbacks, wire and data formats,
397
+ and the lifecycle rules in [contracts.md](contracts.md). The documented session,
398
+ run, workspace and other frontend Python APIs are **provisional**, as listed in
399
+ [public-api.md](public-api.md). Internal helpers, lock scopes and file layout may
400
+ change while preserving the relevant behavior. Record compatibility changes in
401
+ the contract documents, changelog and behavioral tests.
401
402
 
402
- When changing an implementation detail, preserve the contracts above or update
403
- the relevant contract document and tests in the same change.
403
+ A compatibility freeze is not stable-release approval. Physical F4/H7 acceptance,
404
+ a clean release version/dependency-floor pairing, and a Supervisor workload/
405
+ timeout decision remain required; see [release-acceptance.md](release-acceptance.md).
404
406
 
405
407
  ## Non-Goals
406
408