autospan 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (100) hide show
  1. autospan-0.1.0/CONTRIBUTING.md +13 -0
  2. autospan-0.1.0/LICENSE +21 -0
  3. autospan-0.1.0/MANIFEST.in +7 -0
  4. autospan-0.1.0/PKG-INFO +148 -0
  5. autospan-0.1.0/README.md +107 -0
  6. autospan-0.1.0/RELEASING.md +31 -0
  7. autospan-0.1.0/SECURITY.md +11 -0
  8. autospan-0.1.0/docs/ARCHITECTURE.md +166 -0
  9. autospan-0.1.0/docs/CONFIGURATION.md +53 -0
  10. autospan-0.1.0/docs/CONSOLE.md +149 -0
  11. autospan-0.1.0/docs/DESIGN.md +52 -0
  12. autospan-0.1.0/docs/DEVELOPING.md +88 -0
  13. autospan-0.1.0/docs/EVIDENCE.md +84 -0
  14. autospan-0.1.0/docs/PLUGIN_AUTHORING.md +254 -0
  15. autospan-0.1.0/docs/PLUGIN_TRUST.md +25 -0
  16. autospan-0.1.0/docs/SCENARIO_MANUAL.md +272 -0
  17. autospan-0.1.0/pyproject.toml +84 -0
  18. autospan-0.1.0/scripts/check_dist.py +134 -0
  19. autospan-0.1.0/scripts/check_readme.py +68 -0
  20. autospan-0.1.0/scripts/verify_install.py +220 -0
  21. autospan-0.1.0/setup.cfg +4 -0
  22. autospan-0.1.0/src/autospan/__init__.py +11 -0
  23. autospan-0.1.0/src/autospan/__main__.py +5 -0
  24. autospan-0.1.0/src/autospan/_core/__init__.py +1 -0
  25. autospan-0.1.0/src/autospan/_core/active_claims.py +73 -0
  26. autospan-0.1.0/src/autospan/_core/config.py +78 -0
  27. autospan-0.1.0/src/autospan/_core/discovery.py +132 -0
  28. autospan-0.1.0/src/autospan/_core/evidence_store.py +382 -0
  29. autospan-0.1.0/src/autospan/_core/graph.py +46 -0
  30. autospan-0.1.0/src/autospan/_core/handover.py +99 -0
  31. autospan-0.1.0/src/autospan/_core/host.py +155 -0
  32. autospan-0.1.0/src/autospan/_core/periodic.py +487 -0
  33. autospan-0.1.0/src/autospan/_core/planner.py +99 -0
  34. autospan-0.1.0/src/autospan/_core/provider_discovery.py +71 -0
  35. autospan-0.1.0/src/autospan/_core/providers.py +124 -0
  36. autospan-0.1.0/src/autospan/_core/registry.py +53 -0
  37. autospan-0.1.0/src/autospan/_core/resource_claims.py +25 -0
  38. autospan-0.1.0/src/autospan/_core/runtime.py +345 -0
  39. autospan-0.1.0/src/autospan/api/__init__.py +106 -0
  40. autospan-0.1.0/src/autospan/api/commands.py +420 -0
  41. autospan-0.1.0/src/autospan/api/component.py +235 -0
  42. autospan-0.1.0/src/autospan/api/errors.py +29 -0
  43. autospan-0.1.0/src/autospan/api/evidence.py +136 -0
  44. autospan-0.1.0/src/autospan/api/frontend.py +82 -0
  45. autospan-0.1.0/src/autospan/api/gates.py +37 -0
  46. autospan-0.1.0/src/autospan/api/periodic.py +123 -0
  47. autospan-0.1.0/src/autospan/api/plugin.py +90 -0
  48. autospan-0.1.0/src/autospan/api/provider.py +118 -0
  49. autospan-0.1.0/src/autospan/api/runtime.py +85 -0
  50. autospan-0.1.0/src/autospan/capabilities/__init__.py +0 -0
  51. autospan-0.1.0/src/autospan/capabilities/debugger/__init__.py +21 -0
  52. autospan-0.1.0/src/autospan/capabilities/debugger/contract.py +196 -0
  53. autospan-0.1.0/src/autospan/capabilities/debugger/fake.py +210 -0
  54. autospan-0.1.0/src/autospan/capabilities/flasher/__init__.py +11 -0
  55. autospan-0.1.0/src/autospan/capabilities/flasher/contract.py +57 -0
  56. autospan-0.1.0/src/autospan/capabilities/flasher/fake.py +34 -0
  57. autospan-0.1.0/src/autospan/frontends/__init__.py +1 -0
  58. autospan-0.1.0/src/autospan/frontends/cli.py +273 -0
  59. autospan-0.1.0/src/autospan/frontends/console.py +140 -0
  60. autospan-0.1.0/src/autospan/frontends/frontend_host.py +92 -0
  61. autospan-0.1.0/src/autospan/frontends/pytest_plugin.py +212 -0
  62. autospan-0.1.0/src/autospan/frontends/scaffold.py +216 -0
  63. autospan-0.1.0/src/autospan/frontends/test_command.py +226 -0
  64. autospan-0.1.0/src/autospan/frontends/tui/__init__.py +1 -0
  65. autospan-0.1.0/src/autospan/frontends/tui/app.py +406 -0
  66. autospan-0.1.0/src/autospan/frontends/tui/editing.py +38 -0
  67. autospan-0.1.0/src/autospan/py.typed +0 -0
  68. autospan-0.1.0/src/autospan/testing/__init__.py +6 -0
  69. autospan-0.1.0/src/autospan/testing/conformance.py +47 -0
  70. autospan-0.1.0/src/autospan/testing/harness.py +37 -0
  71. autospan-0.1.0/src/autospan/testing/plugin.py +68 -0
  72. autospan-0.1.0/src/autospan.egg-info/PKG-INFO +148 -0
  73. autospan-0.1.0/src/autospan.egg-info/SOURCES.txt +98 -0
  74. autospan-0.1.0/src/autospan.egg-info/dependency_links.txt +1 -0
  75. autospan-0.1.0/src/autospan.egg-info/entry_points.txt +2 -0
  76. autospan-0.1.0/src/autospan.egg-info/requires.txt +18 -0
  77. autospan-0.1.0/src/autospan.egg-info/top_level.txt +1 -0
  78. autospan-0.1.0/tests/test_console.py +185 -0
  79. autospan-0.1.0/tests/test_console_hardening.py +369 -0
  80. autospan-0.1.0/tests/test_evidence.py +230 -0
  81. autospan-0.1.0/tests/test_extension_points.py +652 -0
  82. autospan-0.1.0/tests/test_graph.py +30 -0
  83. autospan-0.1.0/tests/test_handover.py +204 -0
  84. autospan-0.1.0/tests/test_handover_shutdown.py +380 -0
  85. autospan-0.1.0/tests/test_hardening.py +125 -0
  86. autospan-0.1.0/tests/test_kernel_independence.py +92 -0
  87. autospan-0.1.0/tests/test_lifecycle.py +220 -0
  88. autospan-0.1.0/tests/test_live_claims.py +594 -0
  89. autospan-0.1.0/tests/test_periodic.py +484 -0
  90. autospan-0.1.0/tests/test_periodic_shutdown.py +445 -0
  91. autospan-0.1.0/tests/test_plan.py +62 -0
  92. autospan-0.1.0/tests/test_plugin_discovery.py +96 -0
  93. autospan-0.1.0/tests/test_provider_discovery.py +75 -0
  94. autospan-0.1.0/tests/test_providers.py +185 -0
  95. autospan-0.1.0/tests/test_public_api.py +72 -0
  96. autospan-0.1.0/tests/test_resource_claims.py +84 -0
  97. autospan-0.1.0/tests/test_runtime_reentry.py +277 -0
  98. autospan-0.1.0/tests/test_scaffold.py +55 -0
  99. autospan-0.1.0/tests/test_session_runtime.py +224 -0
  100. autospan-0.1.0/tests/test_tui.py +520 -0
@@ -0,0 +1,13 @@
1
+ # Contributing
2
+
3
+ Install the development dependencies and run the checks in
4
+ [the development guide](docs/DEVELOPING.md).
5
+
6
+ Keep the core independent of protocols, vendors and projects. Driver implementations
7
+ and project configuration belong in separate packages. Add a provider contract only
8
+ when real tool substitution requires it and its semantics can stay generic; see
9
+ [the design principles](docs/DESIGN.md).
10
+
11
+ Include tests for behavior changes and update the relevant public docs. Use synthetic
12
+ fixtures and contribute only material you have the right to license under MIT.
13
+ Keep private configuration, credentials and generated evidence out of commits.
autospan-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Nidhal Abidi
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,7 @@
1
+ include CONTRIBUTING.md SECURITY.md RELEASING.md
2
+ recursive-include docs *.md
3
+ recursive-include tests *.py
4
+ recursive-include scripts *.py
5
+ global-exclude __pycache__ *.py[cod]
6
+ prune artifacts
7
+ prune .github
@@ -0,0 +1,148 @@
1
+ Metadata-Version: 2.4
2
+ Name: autospan
3
+ Version: 0.1.0
4
+ Summary: Test automation from hardware to CI
5
+ Author: Nidhal Abidi
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/nidops/autospan
8
+ Project-URL: Documentation, https://github.com/nidops/autospan/blob/master/docs/ARCHITECTURE.md
9
+ Project-URL: Source, https://github.com/nidops/autospan
10
+ Project-URL: Issues, https://github.com/nidops/autospan/issues
11
+ Keywords: testing,test-automation,hardware,pytest,ci,embedded
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Programming Language :: Python :: 3.14
20
+ Classifier: Topic :: Software Development :: Testing
21
+ Classifier: Typing :: Typed
22
+ Requires-Python: >=3.11
23
+ Description-Content-Type: text/markdown
24
+ License-File: LICENSE
25
+ Requires-Dist: PyYAML<7,>=6.0
26
+ Provides-Extra: tui
27
+ Requires-Dist: textual>=0.80; extra == "tui"
28
+ Provides-Extra: test
29
+ Requires-Dist: pytest<10,>=8; extra == "test"
30
+ Provides-Extra: dev
31
+ Requires-Dist: pytest<10,>=8; extra == "dev"
32
+ Requires-Dist: textual>=0.80; extra == "dev"
33
+ Requires-Dist: ruff>=0.12; extra == "dev"
34
+ Requires-Dist: mypy>=1.13; extra == "dev"
35
+ Requires-Dist: types-PyYAML; extra == "dev"
36
+ Requires-Dist: build>=1.2; extra == "dev"
37
+ Requires-Dist: twine<8,>=6.2; extra == "dev"
38
+ Requires-Dist: packaging>=24; extra == "dev"
39
+ Requires-Dist: readme-renderer[md]>=44; extra == "dev"
40
+ Dynamic: license-file
41
+
42
+ # AutoSpan
43
+
44
+ Test automation from hardware to CI.
45
+
46
+ AutoSpan plans and runs host-side tests with explicit resource ownership, predictable
47
+ cleanup and evidence. Project plugins supply the configuration, drivers, scenarios
48
+ and commands for a bench. The core stays independent of protocols, vendors and projects.
49
+
50
+ ```text
51
+ configuration -> project plugin -> runtime plan -> components
52
+ |
53
+ scenarios / console / Python
54
+ |
55
+ events and artifacts
56
+ ```
57
+
58
+ ## Quick start
59
+
60
+ Python 3.11 or newer is required. The package is `autospan`; use the `test` and
61
+ `tui` extras for pytest and Textual. Install from this checkout and create a small
62
+ project with an installed smoke scenario:
63
+
64
+ These commands use a POSIX shell. On Windows, create the environment with
65
+ `python -m venv .venv` and activate it with `.venv\Scripts\Activate.ps1` in PowerShell.
66
+
67
+ ```bash
68
+ python3 -m venv .venv
69
+ source .venv/bin/activate
70
+ python -m pip install -e '.[test,tui]'
71
+ autospan new sample
72
+ python -m pip install -e './autospan-sample[dev]'
73
+ autospan plan --config autospan-sample/autospan.yaml
74
+ autospan test --config autospan-sample/autospan.yaml -- -q
75
+ autospan console --config autospan-sample/autospan.yaml --plain --exec "sample status"
76
+ ```
77
+
78
+ Interactive Textual use requires the `tui` extra and a TTY; the quick start above
79
+ runs in batch mode.
80
+
81
+ Planning validates configuration, dependencies and claims without opening hardware.
82
+ The sample uses no hardware. For a hardware project, enable the gate explicitly:
83
+
84
+ ```bash
85
+ autospan test --config bench.local.yaml -- --hardware -q
86
+ autospan console --config bench.local.yaml --hardware
87
+ ```
88
+
89
+ For `test`, arguments after `--` go to pytest. Console and contributed frontends
90
+ accept their gate options before `--`. Installing a plugin does not activate it;
91
+ its name must appear in the configuration.
92
+
93
+ ## What the runtime owns
94
+
95
+ - Immutable plans, dependency ordering and resource-claim validation.
96
+ - Component startup, rollback after partial failure and reverse-order teardown.
97
+ - One runtime model shared by hosted pytest, the console and Python frontends.
98
+ - Hardware and destructive gates, with optional project gates.
99
+ - Generic periodic tasks with leases, startup checks and failure reporting.
100
+ - Structured evidence and opaque files with correlation, size and SHA-256 metadata.
101
+
102
+ Plugins use the public `autospan.api` package. `autospan._core` is internal.
103
+ Extensions are trusted Python code; the host is not a sandbox or a real-time scheduler.
104
+
105
+ ## Extension points
106
+
107
+ A project plugin owns bench wiring, scenarios, commands and simulation policy:
108
+
109
+ ```toml
110
+ [project.entry-points."autospan.plugins"]
111
+ my-project = "my_project.plugin:PLUGIN"
112
+ ```
113
+
114
+ Plugins declare API version `1`. Their component factories open ordinary driver
115
+ libraries and return project facades. Drivers do not need an AutoSpan entry point.
116
+
117
+ Swappable tools such as flashers and debuggers use a separate provider group:
118
+
119
+ ```toml
120
+ [project.entry-points."autospan.providers"]
121
+ my-flasher = "my_flasher.autospan:PROVIDER"
122
+ ```
123
+
124
+ A plan selects a provider by name through `provider_component(...)`. Discovery is
125
+ lazy: `autospan plugins` and `autospan providers` list installed extensions without
126
+ importing them.
127
+
128
+ Commands use `CommandContext` for the live runtime, explicit output and cooperative
129
+ cancellation. The optional `tui` extra supplies history, completion, a command
130
+ picker and log search/export; Quit waits for the active operation. See the
131
+ [console manual](https://github.com/nidops/autospan/blob/master/docs/CONSOLE.md).
132
+
133
+ ## Evidence
134
+
135
+ Runs write `events.jsonl` and `manifest.json` under `artifacts/evidence/<run-id>/`.
136
+ Use evidence steps for meaningful actions. Keep high-rate traffic in its recorder
137
+ and attach the resulting file. Worker context must be bound explicitly and must
138
+ match the lifetime of the work being recorded.
139
+
140
+ ## Documentation
141
+
142
+ - [Architecture](https://github.com/nidops/autospan/blob/master/docs/ARCHITECTURE.md) and [design principles](https://github.com/nidops/autospan/blob/master/docs/DESIGN.md).
143
+ - [Plugin authoring](https://github.com/nidops/autospan/blob/master/docs/PLUGIN_AUTHORING.md) and [scenario authoring](https://github.com/nidops/autospan/blob/master/docs/SCENARIO_MANUAL.md).
144
+ - [Configuration](https://github.com/nidops/autospan/blob/master/docs/CONFIGURATION.md), [console](https://github.com/nidops/autospan/blob/master/docs/CONSOLE.md) and [evidence](https://github.com/nidops/autospan/blob/master/docs/EVIDENCE.md).
145
+ - [Development](https://github.com/nidops/autospan/blob/master/docs/DEVELOPING.md) and [releasing](https://github.com/nidops/autospan/blob/master/RELEASING.md).
146
+ - [Extension trust](https://github.com/nidops/autospan/blob/master/docs/PLUGIN_TRUST.md) and [security](https://github.com/nidops/autospan/blob/master/SECURITY.md).
147
+
148
+ MIT. See [LICENSE](https://github.com/nidops/autospan/blob/master/LICENSE).
@@ -0,0 +1,107 @@
1
+ # AutoSpan
2
+
3
+ Test automation from hardware to CI.
4
+
5
+ AutoSpan plans and runs host-side tests with explicit resource ownership, predictable
6
+ cleanup and evidence. Project plugins supply the configuration, drivers, scenarios
7
+ and commands for a bench. The core stays independent of protocols, vendors and projects.
8
+
9
+ ```text
10
+ configuration -> project plugin -> runtime plan -> components
11
+ |
12
+ scenarios / console / Python
13
+ |
14
+ events and artifacts
15
+ ```
16
+
17
+ ## Quick start
18
+
19
+ Python 3.11 or newer is required. The package is `autospan`; use the `test` and
20
+ `tui` extras for pytest and Textual. Install from this checkout and create a small
21
+ project with an installed smoke scenario:
22
+
23
+ These commands use a POSIX shell. On Windows, create the environment with
24
+ `python -m venv .venv` and activate it with `.venv\Scripts\Activate.ps1` in PowerShell.
25
+
26
+ ```bash
27
+ python3 -m venv .venv
28
+ source .venv/bin/activate
29
+ python -m pip install -e '.[test,tui]'
30
+ autospan new sample
31
+ python -m pip install -e './autospan-sample[dev]'
32
+ autospan plan --config autospan-sample/autospan.yaml
33
+ autospan test --config autospan-sample/autospan.yaml -- -q
34
+ autospan console --config autospan-sample/autospan.yaml --plain --exec "sample status"
35
+ ```
36
+
37
+ Interactive Textual use requires the `tui` extra and a TTY; the quick start above
38
+ runs in batch mode.
39
+
40
+ Planning validates configuration, dependencies and claims without opening hardware.
41
+ The sample uses no hardware. For a hardware project, enable the gate explicitly:
42
+
43
+ ```bash
44
+ autospan test --config bench.local.yaml -- --hardware -q
45
+ autospan console --config bench.local.yaml --hardware
46
+ ```
47
+
48
+ For `test`, arguments after `--` go to pytest. Console and contributed frontends
49
+ accept their gate options before `--`. Installing a plugin does not activate it;
50
+ its name must appear in the configuration.
51
+
52
+ ## What the runtime owns
53
+
54
+ - Immutable plans, dependency ordering and resource-claim validation.
55
+ - Component startup, rollback after partial failure and reverse-order teardown.
56
+ - One runtime model shared by hosted pytest, the console and Python frontends.
57
+ - Hardware and destructive gates, with optional project gates.
58
+ - Generic periodic tasks with leases, startup checks and failure reporting.
59
+ - Structured evidence and opaque files with correlation, size and SHA-256 metadata.
60
+
61
+ Plugins use the public `autospan.api` package. `autospan._core` is internal.
62
+ Extensions are trusted Python code; the host is not a sandbox or a real-time scheduler.
63
+
64
+ ## Extension points
65
+
66
+ A project plugin owns bench wiring, scenarios, commands and simulation policy:
67
+
68
+ ```toml
69
+ [project.entry-points."autospan.plugins"]
70
+ my-project = "my_project.plugin:PLUGIN"
71
+ ```
72
+
73
+ Plugins declare API version `1`. Their component factories open ordinary driver
74
+ libraries and return project facades. Drivers do not need an AutoSpan entry point.
75
+
76
+ Swappable tools such as flashers and debuggers use a separate provider group:
77
+
78
+ ```toml
79
+ [project.entry-points."autospan.providers"]
80
+ my-flasher = "my_flasher.autospan:PROVIDER"
81
+ ```
82
+
83
+ A plan selects a provider by name through `provider_component(...)`. Discovery is
84
+ lazy: `autospan plugins` and `autospan providers` list installed extensions without
85
+ importing them.
86
+
87
+ Commands use `CommandContext` for the live runtime, explicit output and cooperative
88
+ cancellation. The optional `tui` extra supplies history, completion, a command
89
+ picker and log search/export; Quit waits for the active operation. See the
90
+ [console manual](https://github.com/nidops/autospan/blob/master/docs/CONSOLE.md).
91
+
92
+ ## Evidence
93
+
94
+ Runs write `events.jsonl` and `manifest.json` under `artifacts/evidence/<run-id>/`.
95
+ Use evidence steps for meaningful actions. Keep high-rate traffic in its recorder
96
+ and attach the resulting file. Worker context must be bound explicitly and must
97
+ match the lifetime of the work being recorded.
98
+
99
+ ## Documentation
100
+
101
+ - [Architecture](https://github.com/nidops/autospan/blob/master/docs/ARCHITECTURE.md) and [design principles](https://github.com/nidops/autospan/blob/master/docs/DESIGN.md).
102
+ - [Plugin authoring](https://github.com/nidops/autospan/blob/master/docs/PLUGIN_AUTHORING.md) and [scenario authoring](https://github.com/nidops/autospan/blob/master/docs/SCENARIO_MANUAL.md).
103
+ - [Configuration](https://github.com/nidops/autospan/blob/master/docs/CONFIGURATION.md), [console](https://github.com/nidops/autospan/blob/master/docs/CONSOLE.md) and [evidence](https://github.com/nidops/autospan/blob/master/docs/EVIDENCE.md).
104
+ - [Development](https://github.com/nidops/autospan/blob/master/docs/DEVELOPING.md) and [releasing](https://github.com/nidops/autospan/blob/master/RELEASING.md).
105
+ - [Extension trust](https://github.com/nidops/autospan/blob/master/docs/PLUGIN_TRUST.md) and [security](https://github.com/nidops/autospan/blob/master/SECURITY.md).
106
+
107
+ MIT. See [LICENSE](https://github.com/nidops/autospan/blob/master/LICENSE).
@@ -0,0 +1,31 @@
1
+ # Releasing AutoSpan
2
+
3
+ Package version lives in `pyproject.toml`; `autospan.__version__` reads installed
4
+ metadata. The plugin API version is separate and is currently `1`.
5
+
6
+ ## Setup
7
+
8
+ Configure [trusted publishing](https://docs.pypi.org/trusted-publishers/)
9
+ separately on TestPyPI and PyPI for project `autospan`, repository
10
+ `nidops/autospan`, and workflow `publish.yml`. Use GitHub environments `testpypi`
11
+ and `pypi` respectively, restricted to release tags, with production approval
12
+ where available. For a new project, register a pending publisher; this does not
13
+ reserve the package name. Confirm account ownership and public documentation links.
14
+
15
+ ## Release
16
+
17
+ 1. Update the package version and documentation.
18
+ 2. Run the [development checks](docs/DEVELOPING.md#checks) and
19
+ [distribution checks](docs/DEVELOPING.md#distribution-checks).
20
+ 3. From a clean checkout, tag the reviewed `master` commit as `vX.Y.Z` after all
21
+ supported CI jobs pass. Verify the version with
22
+ `python scripts/check_dist.py dist --tag vX.Y.Z`, substituting the actual version.
23
+ 4. Push the tag, then manually run **Publish** in GitHub Actions with that tag selected.
24
+
25
+ The workflow reruns CI, uploads verified artifacts to TestPyPI, checks the downloaded
26
+ wheel's hash and installation, then publishes the same artifacts to PyPI and verifies
27
+ again. Pushes alone do not publish. Check the package page after completion.
28
+
29
+ If publication fails, rerun failed jobs in the same run while its `release-dist`
30
+ artifact is available. Never overwrite tags or uploaded files, or hide mismatches
31
+ with `skip-existing`. Changed release contents require a new version.
@@ -0,0 +1,11 @@
1
+ # Security policy
2
+
3
+ Report vulnerabilities privately to the maintainers (use the repository's private
4
+ security advisory feature); do not open a public issue.
5
+
6
+ AutoSpan plugins are trusted Python code and are not sandboxed (see
7
+ `docs/PLUGIN_TRUST.md`). Reports about a malicious or compromised plugin are out
8
+ of scope; reports about the kernel itself (path handling in the evidence store,
9
+ configuration parsing, plan validation, gate bypass) are in scope.
10
+
11
+ Supported versions: the latest `0.x` release.
@@ -0,0 +1,166 @@
1
+ # AutoSpan architecture
2
+
3
+ ## Ownership
4
+
5
+ > AutoSpan owns execution. A project plugin owns project meaning. Drivers are plain libraries. Swappable external tools are providers of narrow kernel contracts.
6
+
7
+ ## Runtime model
8
+
9
+ There is one owner per opened runtime. `RuntimeSession` is the internal lifecycle
10
+ owner; `Runtime` is its public live view. Within one Python process, active
11
+ sessions reserve the plan's exclusive/shared resource claims atomically before
12
+ hardware opens, and release claims after teardown or failed startup. This prevents
13
+ function- and session-scoped fixtures from opening the same exclusively claimed
14
+ hardware simultaneously. The registry does **not** coordinate separate processes,
15
+ and different selector aliases still require a common canonical claim identity.
16
+ Shared claims may coexist across sessions, but handover requires all other sessions
17
+ claiming that resource to close first. While an external tool owns it, new claims
18
+ are rejected until reacquisition finishes, including recovery after a failure.
19
+ A project "workspace" is only a directory containing project files/configuration,
20
+ never a second runtime layer. There is no separate `autospan-core` or
21
+ `autospan-workspace` runtime product.
22
+
23
+ ```text
24
+ config
25
+ ↓
26
+ explicitly selected project plugin(s)
27
+ ↓
28
+ declaration-only registration
29
+ ↓
30
+ side-effect-free RuntimePlan
31
+ ↓
32
+ provider resolution + DAG / claims validation
33
+ ↓
34
+ dependency-first component acquisition
35
+ ↓
36
+ one Runtime view
37
+ ├── pytest
38
+ ├── console
39
+ └── Python/frontends
40
+ │
41
+ ├── runtime-owned periodic scheduler
42
+ └── shared evidence sink
43
+ ↓
44
+ stop periodic workers
45
+ ↓
46
+ reverse-order component teardown
47
+ ```
48
+
49
+ Merely installing a project plugin or provider never activates it.
50
+
51
+ Console modes share one dispatcher and the same session:
52
+
53
+ ```text
54
+ plain / batch / Textual -> CommandShell -> CommandContext.runtime -> live components
55
+ | |
56
+ verdict-aware step explicit output / cancellation
57
+ |
58
+ finish/join command -> stop periodic workers -> teardown
59
+ ```
60
+
61
+ Textual runs one command worker and renders a bounded output queue on its UI
62
+ thread. Busy submissions are rejected. A stop request never substitutes for
63
+ joining that worker before the existing runtime closes.
64
+
65
+ ## Package boundary
66
+
67
+ The `autospan` wheel contains orchestration, generic contracts and frontends.
68
+ Protocol/device drivers are separate libraries imported by project packages.
69
+
70
+ The kernel may define a narrow contract for a swappable project-level tool such as a flasher or debugger. Implementations are separate provider packages advertised through `autospan.providers`.
71
+
72
+ ## Project composition
73
+
74
+ A project plugin normally plans one project/bench component. Its factory opens the driver libraries it needs inside an `ExitStack` and returns a project facade/rig:
75
+
76
+ ```text
77
+ project plugin -> compile plan -> project component -> open drivers -> project rig
78
+ ```
79
+
80
+ Do not explode every driver/client into a kernel component merely because `RuntimePlan` exists. Use separate components when independent lifecycle/dependency/resource ownership actually requires it.
81
+
82
+ ## Extension categories
83
+
84
+ AutoSpan has two framework-level extension categories:
85
+
86
+ 1. **Project plugin** (`autospan.plugins`) — configuration compilation, project component factories, scenarios, commands, gates, frontends and simulation policy.
87
+ 2. **Provider** (`autospan.providers`) — one implementation of a narrow kernel contract such as `flasher` or `debugger`.
88
+
89
+ Drivers use normal Python imports. They have no AutoSpan entry point, project config section or plugin dependency declaration.
90
+
91
+ ## Periodic execution
92
+
93
+ Generic recurring execution belongs to the runtime; protocol meaning does not.
94
+ Projects create named periodic tasks from `context.periodic` or `runtime.periodic`.
95
+ Tasks use monotonic scheduling, nested leases, deterministic shutdown, failure
96
+ propagation and basic timing statistics. A lease can optionally wait for one successful
97
+ first callback (`startup_timeout_s`) when a project needs a generic readiness boundary.
98
+ AutoSpan never knows whether the callback is network management, a diagnostic keep-alive,
99
+ serial polling or something else.
100
+
101
+ Periodic workers are stopped before component teardown so a worker cannot continue
102
+ using a driver after its resource has closed. This is host-side orchestration, not a
103
+ hard-real-time scheduler.
104
+ Callbacks must return for shutdown to finish. A slow-stop deadline reports failure;
105
+ the owner still waits for the worker instead of closing its resources underneath it.
106
+ This is intentionally **not** a hard shutdown timeout: Python cannot safely kill a
107
+ blocked thread and revoke its driver's handles. Every periodic callback must use
108
+ finite I/O timeouts and honor its driver's cooperative cancellation policy. If a
109
+ strict stop deadline is required, run the blocking work in a supervised process
110
+ with an explicit termination and ownership-recovery contract.
111
+
112
+ ## Evidence
113
+
114
+ Control-plane events are small and structured. High-rate data stays in the owning
115
+ driver/project recorder and is committed as an opaque artifact. Artifact formats are
116
+ open-ended; the evidence store records metadata, correlation, completeness, size and
117
+ hash without parsing the traffic format.
118
+
119
+ ## Lifecycle
120
+
121
+ Every component is acquired through a context manager. AutoSpan uses dependency order for startup and reverse order for cleanup. Partial startup failures roll back already-opened components.
122
+
123
+ Runtime entry validates resource claims before reserving resources or opening
124
+ components, including for directly constructed plans. Shutdown rejects new
125
+ handovers, stops and joins periodic workers, then waits for admitted handovers
126
+ to finish reacquisition and diagnostics before closing components. Reservations
127
+ remain held until component cleanup has been attempted, including after errors.
128
+ The session can be entered again once cleanup finishes.
129
+
130
+ Shutdown from inside the session's own handover or periodic callback is rejected
131
+ to avoid waiting on the caller itself. Drivers, handover bodies and evidence
132
+ callbacks must return for cleanup to finish; a blocked hardware call cannot be
133
+ safely cancelled by closing its resources from another thread.
134
+
135
+ ## Pytest
136
+
137
+ AutoSpan invokes pytest with explicitly contributed scenario paths and one bridge plugin. Third-party pytest-plugin autoloading is disabled for hosted runs.
138
+
139
+ The function-scoped `runtime` fixture acquires per test for hardware isolation.
140
+ The explicit `session_runtime` fixture acquires once for a shared bench. Use one
141
+ allocation scope per bench; both fixtures use the same owner model.
142
+
143
+ ## Gates
144
+
145
+ The host always knows `hardware` and `destructive` (`destructive` implies `hardware`). Project plugins may contribute more. Gates are enforced both on scenarios and on component acquisition, before a gated factory can touch hardware.
146
+
147
+ ## Frontends
148
+
149
+ `autospan run NAME` executes a project-contributed frontend using the same plan/runtime owner. A frontend must not create a parallel hardware lifecycle.
150
+
151
+ ## Outcome
152
+
153
+ Function-scoped teardown sees the setup/call verdict known at that point; its own
154
+ teardown report arrives later. The session runtime closes after all fixture reports
155
+ and receives the aggregate outcome, including setup and teardown failures. The
156
+ run manifest records the final pytest status. Factories can retain failure-only
157
+ artifacts using `ComponentContext.outcome`.
158
+
159
+ ## Non-goals
160
+
161
+ - no runtime Git/package fetching;
162
+ - no plugin dependency graph for drivers;
163
+ - no distributed executor;
164
+ - no hard-real-time guarantee (including periodic workers);
165
+ - no universal transport or device hierarchy;
166
+ - no protocol semantics in the kernel.
@@ -0,0 +1,53 @@
1
+ # Configuration
2
+
3
+ AutoSpan owns only the host section. Each selected project plugin owns and strictly validates its own section.
4
+
5
+ ```yaml
6
+ autospan:
7
+ plugins: [my-project]
8
+
9
+ my-project:
10
+ port: sim
11
+ ```
12
+
13
+ `autospan` currently accepts only `plugins` and `pytest`. Unknown host keys are errors. Project sections are validated by their owning project package; unknown project keys should likewise fail rather than silently default.
14
+
15
+ Project plugins are loaded only when explicitly named. Installing a driver, provider or another project package does not activate it.
16
+
17
+ ## Command-line overrides
18
+
19
+ Every config-aware command accepts repeatable `--set dotted.key=value`. Values are parsed as YAML and applied before project loading:
20
+
21
+ ```bash
22
+ autospan test --config bench.yaml --set my-project.bench_config=C:/benches/b12.yaml
23
+ autospan plan --config bench.yaml --set autospan.plugins=[my-project]
24
+ ```
25
+
26
+ ## Hosted pytest settings
27
+
28
+ `autospan test` never reads an arbitrary project `pytest.ini`/`pyproject.toml`. It writes one `pytest.ini` per run from:
29
+
30
+ 1. AutoSpan defaults;
31
+ 2. `ScenarioContribution.pytest_ini` defaults from selected projects;
32
+ 3. operator `autospan.pytest.ini` overrides.
33
+
34
+ Extra pytest plugins must be explicit because autoload is disabled:
35
+
36
+ ```yaml
37
+ autospan:
38
+ plugins: [my-project]
39
+ pytest:
40
+ plugins: [pytest_metadata.plugin]
41
+ ini:
42
+ log_cli_level: DEBUG
43
+ ```
44
+
45
+ `cache_dir`, `testpaths`, `rootdir` and `confcutdir` are owned by AutoSpan.
46
+
47
+ Planning is side-effect free. Invalid component dependencies, cycles, resource collisions, missing providers, unknown component types and malformed configuration must fail before hardware opens.
48
+
49
+ Relative project paths should be resolved against `PlanBuilder.config_dir`, the directory containing the selected AutoSpan config file.
50
+
51
+ Keep machine-specific settings in ignored files such as `bench.local.yaml`.
52
+ For credentials, store an environment-variable name in reviewed config and resolve
53
+ its value at runtime in project code.