PyTaskWireCheck 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 (62) hide show
  1. pytaskwirecheck-0.1.0/LICENSE +21 -0
  2. pytaskwirecheck-0.1.0/PKG-INFO +316 -0
  3. pytaskwirecheck-0.1.0/README.md +276 -0
  4. pytaskwirecheck-0.1.0/pyproject.toml +108 -0
  5. pytaskwirecheck-0.1.0/setup.cfg +4 -0
  6. pytaskwirecheck-0.1.0/src/PyTaskWireCheck.egg-info/PKG-INFO +316 -0
  7. pytaskwirecheck-0.1.0/src/PyTaskWireCheck.egg-info/SOURCES.txt +60 -0
  8. pytaskwirecheck-0.1.0/src/PyTaskWireCheck.egg-info/dependency_links.txt +1 -0
  9. pytaskwirecheck-0.1.0/src/PyTaskWireCheck.egg-info/entry_points.txt +2 -0
  10. pytaskwirecheck-0.1.0/src/PyTaskWireCheck.egg-info/requires.txt +13 -0
  11. pytaskwirecheck-0.1.0/src/PyTaskWireCheck.egg-info/top_level.txt +1 -0
  12. pytaskwirecheck-0.1.0/src/pytaskwirecheck/__init__.py +32 -0
  13. pytaskwirecheck-0.1.0/src/pytaskwirecheck/api.py +314 -0
  14. pytaskwirecheck-0.1.0/src/pytaskwirecheck/cache/__init__.py +0 -0
  15. pytaskwirecheck-0.1.0/src/pytaskwirecheck/cache/fingerprint.py +176 -0
  16. pytaskwirecheck-0.1.0/src/pytaskwirecheck/cache/policy.py +49 -0
  17. pytaskwirecheck-0.1.0/src/pytaskwirecheck/cache/store.py +367 -0
  18. pytaskwirecheck-0.1.0/src/pytaskwirecheck/cli.py +690 -0
  19. pytaskwirecheck-0.1.0/src/pytaskwirecheck/config/__init__.py +0 -0
  20. pytaskwirecheck-0.1.0/src/pytaskwirecheck/config/loader.py +72 -0
  21. pytaskwirecheck-0.1.0/src/pytaskwirecheck/config/schema.py +170 -0
  22. pytaskwirecheck-0.1.0/src/pytaskwirecheck/config/validation.py +590 -0
  23. pytaskwirecheck-0.1.0/src/pytaskwirecheck/diagnostics/__init__.py +31 -0
  24. pytaskwirecheck-0.1.0/src/pytaskwirecheck/diagnostics/errors.py +86 -0
  25. pytaskwirecheck-0.1.0/src/pytaskwirecheck/engine/__init__.py +0 -0
  26. pytaskwirecheck-0.1.0/src/pytaskwirecheck/engine/cancellation.py +38 -0
  27. pytaskwirecheck-0.1.0/src/pytaskwirecheck/engine/events.py +93 -0
  28. pytaskwirecheck-0.1.0/src/pytaskwirecheck/engine/executor.py +301 -0
  29. pytaskwirecheck-0.1.0/src/pytaskwirecheck/engine/scheduler.py +312 -0
  30. pytaskwirecheck-0.1.0/src/pytaskwirecheck/engine/state.py +123 -0
  31. pytaskwirecheck-0.1.0/src/pytaskwirecheck/graph/__init__.py +0 -0
  32. pytaskwirecheck-0.1.0/src/pytaskwirecheck/graph/builder.py +33 -0
  33. pytaskwirecheck-0.1.0/src/pytaskwirecheck/graph/model.py +104 -0
  34. pytaskwirecheck-0.1.0/src/pytaskwirecheck/graph/planner.py +155 -0
  35. pytaskwirecheck-0.1.0/src/pytaskwirecheck/graph/validator.py +92 -0
  36. pytaskwirecheck-0.1.0/src/pytaskwirecheck/history/__init__.py +0 -0
  37. pytaskwirecheck-0.1.0/src/pytaskwirecheck/history/store.py +164 -0
  38. pytaskwirecheck-0.1.0/src/pytaskwirecheck/logging/__init__.py +0 -0
  39. pytaskwirecheck-0.1.0/src/pytaskwirecheck/logging/artifacts.py +95 -0
  40. pytaskwirecheck-0.1.0/src/pytaskwirecheck/logging/structured.py +83 -0
  41. pytaskwirecheck-0.1.0/src/pytaskwirecheck/notifications/__init__.py +0 -0
  42. pytaskwirecheck-0.1.0/src/pytaskwirecheck/notifications/base.py +42 -0
  43. pytaskwirecheck-0.1.0/src/pytaskwirecheck/notifications/webhook.py +71 -0
  44. pytaskwirecheck-0.1.0/src/pytaskwirecheck/processes/__init__.py +0 -0
  45. pytaskwirecheck-0.1.0/src/pytaskwirecheck/processes/runner.py +265 -0
  46. pytaskwirecheck-0.1.0/src/pytaskwirecheck/processes/signals.py +65 -0
  47. pytaskwirecheck-0.1.0/src/pytaskwirecheck/py.typed +0 -0
  48. pytaskwirecheck-0.1.0/src/pytaskwirecheck/tui/__init__.py +0 -0
  49. pytaskwirecheck-0.1.0/src/pytaskwirecheck/tui/app.py +204 -0
  50. pytaskwirecheck-0.1.0/src/pytaskwirecheck/tui/renderer.py +143 -0
  51. pytaskwirecheck-0.1.0/tests/test_api.py +159 -0
  52. pytaskwirecheck-0.1.0/tests/test_cache.py +253 -0
  53. pytaskwirecheck-0.1.0/tests/test_cli.py +226 -0
  54. pytaskwirecheck-0.1.0/tests/test_config.py +281 -0
  55. pytaskwirecheck-0.1.0/tests/test_graph.py +151 -0
  56. pytaskwirecheck-0.1.0/tests/test_history.py +116 -0
  57. pytaskwirecheck-0.1.0/tests/test_integration.py +115 -0
  58. pytaskwirecheck-0.1.0/tests/test_notifications.py +135 -0
  59. pytaskwirecheck-0.1.0/tests/test_process_runner.py +191 -0
  60. pytaskwirecheck-0.1.0/tests/test_scheduler.py +284 -0
  61. pytaskwirecheck-0.1.0/tests/test_state.py +51 -0
  62. pytaskwirecheck-0.1.0/tests/test_tui.py +92 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 PyTaskWireCheck Contributors
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,316 @@
1
+ Metadata-Version: 2.4
2
+ Name: PyTaskWireCheck
3
+ Version: 0.1.0
4
+ Summary: Local-first, dependency-aware task orchestration engine (DAG scheduler + content-addressed cache + CLI).
5
+ Author: Georgi Mullassery
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/Mullassery/PyTaskWireCheck
8
+ Project-URL: Repository, https://github.com/Mullassery/PyTaskWireCheck
9
+ Project-URL: Issues, https://github.com/Mullassery/PyTaskWireCheck/issues
10
+ Project-URL: Changelog, https://github.com/Mullassery/PyTaskWireCheck/blob/main/CHANGELOG.md
11
+ Project-URL: Documentation, https://github.com/Mullassery/PyTaskWireCheck#readme
12
+ Keywords: task,tasks,orchestration,dag,workflow,scheduler,pipeline,build,local-first,cli
13
+ Classifier: Development Status :: 3 - Alpha
14
+ Classifier: Environment :: Console
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: Operating System :: OS Independent
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.9
19
+ Classifier: Programming Language :: Python :: 3.10
20
+ Classifier: Programming Language :: Python :: 3.11
21
+ Classifier: Programming Language :: Python :: 3.12
22
+ Classifier: Programming Language :: Python :: 3.13
23
+ Classifier: Topic :: Software Development :: Build Tools
24
+ Classifier: Topic :: System :: Systems Administration
25
+ Classifier: Topic :: Utilities
26
+ Classifier: Typing :: Typed
27
+ Requires-Python: >=3.9
28
+ Description-Content-Type: text/markdown
29
+ License-File: LICENSE
30
+ Requires-Dist: tomli>=2.0; python_version < "3.11"
31
+ Provides-Extra: dev
32
+ Requires-Dist: pytest>=7.0; extra == "dev"
33
+ Requires-Dist: pytest-cov>=4.0; extra == "dev"
34
+ Requires-Dist: ruff>=0.1.0; extra == "dev"
35
+ Requires-Dist: mypy>=1.0; extra == "dev"
36
+ Provides-Extra: release
37
+ Requires-Dist: build>=1.0; extra == "release"
38
+ Requires-Dist: twine>=4.0; extra == "release"
39
+ Dynamic: license-file
40
+
41
+ <div align="center">
42
+
43
+ # PyTaskWireCheck
44
+
45
+ **Local-first, dependency-aware task orchestration for Python.**
46
+
47
+ Describe your tasks and how they depend on each other. PyTaskWireCheck works out the
48
+ order, runs independent work in parallel, retries the flaky parts, skips work
49
+ that hasn't changed, and tells you exactly why — without a server, a daemon,
50
+ or a single byte leaving your machine.
51
+
52
+ [![CI](https://github.com/Mullassery/PyTaskWireCheck/actions/workflows/ci.yml/badge.svg)](https://github.com/Mullassery/PyTaskWireCheck/actions/workflows/ci.yml)
53
+ [![PyPI](https://img.shields.io/pypi/v/PyTaskWireCheck.svg)](https://pypi.org/project/PyTaskWireCheck/)
54
+ [![Python versions](https://img.shields.io/pypi/pyversions/PyTaskWireCheck.svg)](https://pypi.org/project/PyTaskWireCheck/)
55
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
56
+ [![Typed](https://img.shields.io/badge/typing-py.typed-blue.svg)](src/pytaskwirecheck/py.typed)
57
+
58
+ </div>
59
+
60
+ ---
61
+
62
+ ## Why PyTaskWireCheck
63
+
64
+ Most build tools want to own your project. PyTaskWireCheck does one job: it orchestrates
65
+ *other* programs. You already have the scripts, the compilers, the test runners,
66
+ the data jobs — PyTaskWireCheck just knows which ones can run at once, which must wait,
67
+ and which can be skipped because nothing they depend on has changed.
68
+
69
+ - **Local-first.** No server, no account, no telemetry. State lives in
70
+ `.pytaskwirecheck/` inside your project.
71
+ - **Dependency-aware.** Tasks declare `depends_on`; PyTaskWireCheck topologically sorts
72
+ them and runs every independent branch concurrently.
73
+ - **Content-addressed cache.** Tasks declare `inputs` and `outputs`. If the
74
+ fingerprint is unchanged, the task is skipped and its outputs restored.
75
+ - **Deterministic failure handling.** `fail_fast` or continue-and-block, with a
76
+ documented state machine — never a half-observed status.
77
+ - **Scriptable and embeddable.** A first-class CLI *and* a small Python API.
78
+ - **Zero runtime dependencies** on Python 3.11+, one tiny backport otherwise.
79
+
80
+ ## Install
81
+
82
+ ```bash
83
+ pip install PyTaskWireCheck
84
+ # or, for the bleeding edge:
85
+ pip install "PyTaskWireCheck @ git+https://github.com/Mullassery/PyTaskWireCheck"
86
+ ```
87
+
88
+ PyTaskWireCheck targets Python **3.9 – 3.13**. The import name and the command are both
89
+ `pytaskwirecheck`.
90
+
91
+ ```bash
92
+ pytaskwirecheck --version
93
+ ```
94
+
95
+ ## Quick start
96
+
97
+ ```bash
98
+ pytaskwirecheck init # write a starter pytaskwirecheck.toml
99
+ pytaskwirecheck validate # check the config and the graph
100
+ pytaskwirecheck graph # print the DAG (text, dot, or json)
101
+ pytaskwirecheck plan # show levels, order, and max parallelism
102
+ pytaskwirecheck run # run everything, in dependency order
103
+ ```
104
+
105
+ A minimal `pytaskwirecheck.toml`:
106
+
107
+ ```toml
108
+ [project]
109
+ name = "example-workflow"
110
+ version = 1
111
+
112
+ [execution]
113
+ max_workers = 4
114
+ fail_fast = false
115
+
116
+ [tasks.fetch]
117
+ command = ["python", "scripts/fetch.py"]
118
+ outputs = ["data/raw.json"]
119
+
120
+ [tasks.transform]
121
+ command = ["python", "scripts/transform.py"]
122
+ depends_on = ["fetch"]
123
+ inputs = ["data/raw.json"]
124
+ outputs = ["data/clean.parquet"]
125
+
126
+ [tasks.test]
127
+ command = ["pytest", "-q"]
128
+ depends_on = ["transform"]
129
+ ```
130
+
131
+ Run it:
132
+
133
+ ```console
134
+ $ pytaskwirecheck run
135
+ run 3f9c1a2b4d5e
136
+ fetch running
137
+ transform pending
138
+ test pending
139
+ fetch succeeded 0.84s
140
+ transform running
141
+ transform succeeded 1.20s
142
+ test running
143
+ test succeeded 3.05s
144
+ ok: 3 succeeded in 5.11s
145
+ ```
146
+
147
+ ## Features at a glance
148
+
149
+ | Area | What you get |
150
+ | --- | --- |
151
+ | **Graph** | `depends_on`, cycle detection, missing-dependency detection, topological levels |
152
+ | **Scheduling** | bounded concurrency, deterministic ordering, `fail_fast` or continue-and-block |
153
+ | **Processes** | streaming output, process groups, timeouts, cooperative cancellation, output budget |
154
+ | **Retries** | bounded attempts, fixed or exponential backoff, retry-on-exit-code |
155
+ | **Cache** | content-addressed, sha256-verified, atomic commits, corrupt-entry recovery |
156
+ | **CLI** | `init` `validate` `graph` `plan` `run` `cache` `explain` `history`, each with `--json` |
157
+ | **TUI** | optional live dashboard when attached to a TTY (`run --tui`) |
158
+ | **Persistence** | local run history (SQLite, WAL) and optional webhook notifications |
159
+ | **Library** | `Workflow.from_file(...).run()` and functional helpers |
160
+
161
+ ## Command-line reference
162
+
163
+ ```
164
+ pytaskwirecheck [-C ROOT] [--config PATH] [--no-color] COMMAND ...
165
+
166
+ init [--path DIR] [--force] create a starter configuration
167
+ validate [--json] validate configuration and graph
168
+ graph [--format text|dot|json] display the task DAG
169
+ plan [TASKS...] [--json] show the execution plan
170
+ run [TASKS...] [--no-cache] [--max-workers N] [--fail-fast|--no-fail-fast]
171
+ [--json] [-q] [--no-output] [--tui]
172
+ cache status [--json] show cache statistics
173
+ cache clean [--force] [--json] remove cached data
174
+ explain TASK [--json] explain scheduling and cache status for a task
175
+ history [--limit N] [--json] show recent execution summaries
176
+ ```
177
+
178
+ `run` accepts task names and runs only those tasks plus their dependencies. Add
179
+ `--json` for a machine-readable summary, and `-q` to silence progress.
180
+
181
+ ### Exit codes
182
+
183
+ | Code | Meaning |
184
+ | --- | --- |
185
+ | `0` | success |
186
+ | `1` | one or more tasks failed |
187
+ | `2` | usage or configuration error |
188
+ | `3` | graph error (cycle, missing dependency, unknown target) |
189
+ | `130` | interrupted (`Ctrl-C`) |
190
+
191
+ ## Python API
192
+
193
+ ```python
194
+ from pytaskwirecheck import Workflow
195
+
196
+ workflow = Workflow.from_file("pytaskwirecheck.toml")
197
+
198
+ # Inspect before running.
199
+ plan = workflow.plan(["test"])
200
+ print(plan.order, plan.max_parallelism)
201
+
202
+ # Run and react to events.
203
+ def on_event(event):
204
+ print(type(event).__name__, event)
205
+
206
+ result = workflow.run(["test"], on_event=on_event)
207
+ if not result.succeeded:
208
+ for task_id, record in result.records.items():
209
+ if not record.status.is_success:
210
+ print(task_id, record.status.value, record.exit_code)
211
+
212
+ raise SystemExit(0 if result.succeeded else 1)
213
+ ```
214
+
215
+ Functional helpers are available for one-liners:
216
+
217
+ ```python
218
+ import pytaskwirecheck
219
+
220
+ plan = pytaskwirecheck.validate(root=".") # -> ExecutionPlan
221
+ result = pytaskwirecheck.run(["build"], path="ci.toml")
222
+ ```
223
+
224
+ `Workflow.run` supports `use_cache`, `max_workers`, `fail_fast`, `on_event`,
225
+ `cancel_token`, `record_history`, and `notify`. See
226
+ [docs/python-api.md](docs/python-api.md).
227
+
228
+ ## Caching model
229
+
230
+ The cache is **content-addressed** and conservative by design:
231
+
232
+ - A task is cache-eligible only if it declares `inputs` **or** `outputs`
233
+ (otherwise its side effects are unknown, so PyTaskWireCheck never guesses).
234
+ - The fingerprint covers the command, resolved inputs (hashing file contents),
235
+ declared outputs' expectations, relevant environment variables, the config
236
+ schema version, and the cache keys of upstream dependencies.
237
+ - A hit restores declared outputs, but only after verifying every stored file's
238
+ sha256 and size; anything corrupt is discarded rather than trusted.
239
+ - Commits are atomic (staged, then `os.replace`), and protected by a
240
+ cross-process lock. Restores are confined to the workspace root.
241
+ - Environment values are **hashed, never stored in plaintext**.
242
+
243
+ Disable it globally (`[cache] enabled = false`), per task (`cache = false`), or
244
+ per run (`pytaskwirecheck run --no-cache`). Inspect decisions with
245
+ `pytaskwirecheck explain TASK`.
246
+
247
+ ## Execution semantics
248
+
249
+ Every task moves through an explicit state machine:
250
+
251
+ ```
252
+ PENDING → READY → RUNNING → SUCCEEDED | FAILED | TIMED_OUT | CACHED
253
+ ↘ CANCELLED
254
+ dependents of a failure → BLOCKED
255
+ ```
256
+
257
+ - **`fail_fast = true`** stops launching new tasks and cancels in-flight work as
258
+ soon as a task fails.
259
+ - **`fail_fast = false`** lets unrelated branches finish; only the transitive
260
+ dependents of the failed task become `BLOCKED`.
261
+ - A cancelled, timed-out, or failed task is **never cached**.
262
+ - Persistence (history, notifications) is best-effort and **never** changes a
263
+ run's outcome or exit code.
264
+
265
+ ## Architecture
266
+
267
+ ```
268
+ pytaskwirecheck.toml
269
+ │
270
+ ▼
271
+ config ──▶ graph ──▶ planner ──▶ scheduler ──▶ executor ──▶ processes
272
+ (schema, (DAG, (levels, (bounded (retries, (streaming,
273
+ validation) builder) order) concurrency) cache hooks) process groups)
274
+ │
275
+ ▼
276
+ events ──▶ CLI / TUI / logs / history
277
+ ```
278
+
279
+ Each layer depends only on the one below it. The scheduler owns state
280
+ transitions; the executor owns retries and cache interaction; the process runner
281
+ owns the subprocess. Rendering and persistence are observers, never participants.
282
+ Read the full design in [docs/architecture.md](docs/architecture.md).
283
+
284
+ ## Documentation
285
+
286
+ | Document | Contents |
287
+ | --- | --- |
288
+ | [docs/architecture.md](docs/architecture.md) | Layers, data flow, state machine, design rules |
289
+ | [docs/configuration.md](docs/configuration.md) | Every `pytaskwirecheck.toml` field |
290
+ | [docs/execution.md](docs/execution.md) | Scheduling, retries, timeouts, cancellation |
291
+ | [docs/caching.md](docs/caching.md) | Fingerprints, storage, integrity, eviction |
292
+ | [docs/cli.md](docs/cli.md) | Commands, flags, exit codes |
293
+ | [docs/python-api.md](docs/python-api.md) | Embedding PyTaskWireCheck as a library |
294
+ | [docs/security.md](docs/security.md) | Trust model and hardening |
295
+ | [docs/development.md](docs/development.md) | Contributing, tests, tooling |
296
+ | [docs/roadmap.md](docs/roadmap.md) | Where PyTaskWireCheck is going |
297
+ | [docs/implementation-status.md](docs/implementation-status.md) | What is done and verified |
298
+
299
+ ## Status
300
+
301
+ PyTaskWireCheck is **0.1.0** — a complete, tested horizontal slice: config → graph →
302
+ scheduler → runner → cache → CLI → TUI → history → notifications. See
303
+ [docs/implementation-status.md](docs/implementation-status.md) for the verified
304
+ breakdown. It is pre-1.0, so the configuration schema is versioned (`version = 1`)
305
+ and unsupported versions are rejected rather than guessed at.
306
+
307
+ ## Contributing
308
+
309
+ Issues and pull requests are welcome. Start with
310
+ [CONTRIBUTING.md](CONTRIBUTING.md) and [docs/development.md](docs/development.md).
311
+ Please follow the [Code of Conduct](CODE_OF_CONDUCT.md) and report
312
+ vulnerabilities via [SECURITY.md](SECURITY.md).
313
+
314
+ ## License
315
+
316
+ MIT — see [LICENSE](LICENSE).
@@ -0,0 +1,276 @@
1
+ <div align="center">
2
+
3
+ # PyTaskWireCheck
4
+
5
+ **Local-first, dependency-aware task orchestration for Python.**
6
+
7
+ Describe your tasks and how they depend on each other. PyTaskWireCheck works out the
8
+ order, runs independent work in parallel, retries the flaky parts, skips work
9
+ that hasn't changed, and tells you exactly why — without a server, a daemon,
10
+ or a single byte leaving your machine.
11
+
12
+ [![CI](https://github.com/Mullassery/PyTaskWireCheck/actions/workflows/ci.yml/badge.svg)](https://github.com/Mullassery/PyTaskWireCheck/actions/workflows/ci.yml)
13
+ [![PyPI](https://img.shields.io/pypi/v/PyTaskWireCheck.svg)](https://pypi.org/project/PyTaskWireCheck/)
14
+ [![Python versions](https://img.shields.io/pypi/pyversions/PyTaskWireCheck.svg)](https://pypi.org/project/PyTaskWireCheck/)
15
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
16
+ [![Typed](https://img.shields.io/badge/typing-py.typed-blue.svg)](src/pytaskwirecheck/py.typed)
17
+
18
+ </div>
19
+
20
+ ---
21
+
22
+ ## Why PyTaskWireCheck
23
+
24
+ Most build tools want to own your project. PyTaskWireCheck does one job: it orchestrates
25
+ *other* programs. You already have the scripts, the compilers, the test runners,
26
+ the data jobs — PyTaskWireCheck just knows which ones can run at once, which must wait,
27
+ and which can be skipped because nothing they depend on has changed.
28
+
29
+ - **Local-first.** No server, no account, no telemetry. State lives in
30
+ `.pytaskwirecheck/` inside your project.
31
+ - **Dependency-aware.** Tasks declare `depends_on`; PyTaskWireCheck topologically sorts
32
+ them and runs every independent branch concurrently.
33
+ - **Content-addressed cache.** Tasks declare `inputs` and `outputs`. If the
34
+ fingerprint is unchanged, the task is skipped and its outputs restored.
35
+ - **Deterministic failure handling.** `fail_fast` or continue-and-block, with a
36
+ documented state machine — never a half-observed status.
37
+ - **Scriptable and embeddable.** A first-class CLI *and* a small Python API.
38
+ - **Zero runtime dependencies** on Python 3.11+, one tiny backport otherwise.
39
+
40
+ ## Install
41
+
42
+ ```bash
43
+ pip install PyTaskWireCheck
44
+ # or, for the bleeding edge:
45
+ pip install "PyTaskWireCheck @ git+https://github.com/Mullassery/PyTaskWireCheck"
46
+ ```
47
+
48
+ PyTaskWireCheck targets Python **3.9 – 3.13**. The import name and the command are both
49
+ `pytaskwirecheck`.
50
+
51
+ ```bash
52
+ pytaskwirecheck --version
53
+ ```
54
+
55
+ ## Quick start
56
+
57
+ ```bash
58
+ pytaskwirecheck init # write a starter pytaskwirecheck.toml
59
+ pytaskwirecheck validate # check the config and the graph
60
+ pytaskwirecheck graph # print the DAG (text, dot, or json)
61
+ pytaskwirecheck plan # show levels, order, and max parallelism
62
+ pytaskwirecheck run # run everything, in dependency order
63
+ ```
64
+
65
+ A minimal `pytaskwirecheck.toml`:
66
+
67
+ ```toml
68
+ [project]
69
+ name = "example-workflow"
70
+ version = 1
71
+
72
+ [execution]
73
+ max_workers = 4
74
+ fail_fast = false
75
+
76
+ [tasks.fetch]
77
+ command = ["python", "scripts/fetch.py"]
78
+ outputs = ["data/raw.json"]
79
+
80
+ [tasks.transform]
81
+ command = ["python", "scripts/transform.py"]
82
+ depends_on = ["fetch"]
83
+ inputs = ["data/raw.json"]
84
+ outputs = ["data/clean.parquet"]
85
+
86
+ [tasks.test]
87
+ command = ["pytest", "-q"]
88
+ depends_on = ["transform"]
89
+ ```
90
+
91
+ Run it:
92
+
93
+ ```console
94
+ $ pytaskwirecheck run
95
+ run 3f9c1a2b4d5e
96
+ fetch running
97
+ transform pending
98
+ test pending
99
+ fetch succeeded 0.84s
100
+ transform running
101
+ transform succeeded 1.20s
102
+ test running
103
+ test succeeded 3.05s
104
+ ok: 3 succeeded in 5.11s
105
+ ```
106
+
107
+ ## Features at a glance
108
+
109
+ | Area | What you get |
110
+ | --- | --- |
111
+ | **Graph** | `depends_on`, cycle detection, missing-dependency detection, topological levels |
112
+ | **Scheduling** | bounded concurrency, deterministic ordering, `fail_fast` or continue-and-block |
113
+ | **Processes** | streaming output, process groups, timeouts, cooperative cancellation, output budget |
114
+ | **Retries** | bounded attempts, fixed or exponential backoff, retry-on-exit-code |
115
+ | **Cache** | content-addressed, sha256-verified, atomic commits, corrupt-entry recovery |
116
+ | **CLI** | `init` `validate` `graph` `plan` `run` `cache` `explain` `history`, each with `--json` |
117
+ | **TUI** | optional live dashboard when attached to a TTY (`run --tui`) |
118
+ | **Persistence** | local run history (SQLite, WAL) and optional webhook notifications |
119
+ | **Library** | `Workflow.from_file(...).run()` and functional helpers |
120
+
121
+ ## Command-line reference
122
+
123
+ ```
124
+ pytaskwirecheck [-C ROOT] [--config PATH] [--no-color] COMMAND ...
125
+
126
+ init [--path DIR] [--force] create a starter configuration
127
+ validate [--json] validate configuration and graph
128
+ graph [--format text|dot|json] display the task DAG
129
+ plan [TASKS...] [--json] show the execution plan
130
+ run [TASKS...] [--no-cache] [--max-workers N] [--fail-fast|--no-fail-fast]
131
+ [--json] [-q] [--no-output] [--tui]
132
+ cache status [--json] show cache statistics
133
+ cache clean [--force] [--json] remove cached data
134
+ explain TASK [--json] explain scheduling and cache status for a task
135
+ history [--limit N] [--json] show recent execution summaries
136
+ ```
137
+
138
+ `run` accepts task names and runs only those tasks plus their dependencies. Add
139
+ `--json` for a machine-readable summary, and `-q` to silence progress.
140
+
141
+ ### Exit codes
142
+
143
+ | Code | Meaning |
144
+ | --- | --- |
145
+ | `0` | success |
146
+ | `1` | one or more tasks failed |
147
+ | `2` | usage or configuration error |
148
+ | `3` | graph error (cycle, missing dependency, unknown target) |
149
+ | `130` | interrupted (`Ctrl-C`) |
150
+
151
+ ## Python API
152
+
153
+ ```python
154
+ from pytaskwirecheck import Workflow
155
+
156
+ workflow = Workflow.from_file("pytaskwirecheck.toml")
157
+
158
+ # Inspect before running.
159
+ plan = workflow.plan(["test"])
160
+ print(plan.order, plan.max_parallelism)
161
+
162
+ # Run and react to events.
163
+ def on_event(event):
164
+ print(type(event).__name__, event)
165
+
166
+ result = workflow.run(["test"], on_event=on_event)
167
+ if not result.succeeded:
168
+ for task_id, record in result.records.items():
169
+ if not record.status.is_success:
170
+ print(task_id, record.status.value, record.exit_code)
171
+
172
+ raise SystemExit(0 if result.succeeded else 1)
173
+ ```
174
+
175
+ Functional helpers are available for one-liners:
176
+
177
+ ```python
178
+ import pytaskwirecheck
179
+
180
+ plan = pytaskwirecheck.validate(root=".") # -> ExecutionPlan
181
+ result = pytaskwirecheck.run(["build"], path="ci.toml")
182
+ ```
183
+
184
+ `Workflow.run` supports `use_cache`, `max_workers`, `fail_fast`, `on_event`,
185
+ `cancel_token`, `record_history`, and `notify`. See
186
+ [docs/python-api.md](docs/python-api.md).
187
+
188
+ ## Caching model
189
+
190
+ The cache is **content-addressed** and conservative by design:
191
+
192
+ - A task is cache-eligible only if it declares `inputs` **or** `outputs`
193
+ (otherwise its side effects are unknown, so PyTaskWireCheck never guesses).
194
+ - The fingerprint covers the command, resolved inputs (hashing file contents),
195
+ declared outputs' expectations, relevant environment variables, the config
196
+ schema version, and the cache keys of upstream dependencies.
197
+ - A hit restores declared outputs, but only after verifying every stored file's
198
+ sha256 and size; anything corrupt is discarded rather than trusted.
199
+ - Commits are atomic (staged, then `os.replace`), and protected by a
200
+ cross-process lock. Restores are confined to the workspace root.
201
+ - Environment values are **hashed, never stored in plaintext**.
202
+
203
+ Disable it globally (`[cache] enabled = false`), per task (`cache = false`), or
204
+ per run (`pytaskwirecheck run --no-cache`). Inspect decisions with
205
+ `pytaskwirecheck explain TASK`.
206
+
207
+ ## Execution semantics
208
+
209
+ Every task moves through an explicit state machine:
210
+
211
+ ```
212
+ PENDING → READY → RUNNING → SUCCEEDED | FAILED | TIMED_OUT | CACHED
213
+ ↘ CANCELLED
214
+ dependents of a failure → BLOCKED
215
+ ```
216
+
217
+ - **`fail_fast = true`** stops launching new tasks and cancels in-flight work as
218
+ soon as a task fails.
219
+ - **`fail_fast = false`** lets unrelated branches finish; only the transitive
220
+ dependents of the failed task become `BLOCKED`.
221
+ - A cancelled, timed-out, or failed task is **never cached**.
222
+ - Persistence (history, notifications) is best-effort and **never** changes a
223
+ run's outcome or exit code.
224
+
225
+ ## Architecture
226
+
227
+ ```
228
+ pytaskwirecheck.toml
229
+ │
230
+ ▼
231
+ config ──▶ graph ──▶ planner ──▶ scheduler ──▶ executor ──▶ processes
232
+ (schema, (DAG, (levels, (bounded (retries, (streaming,
233
+ validation) builder) order) concurrency) cache hooks) process groups)
234
+ │
235
+ ▼
236
+ events ──▶ CLI / TUI / logs / history
237
+ ```
238
+
239
+ Each layer depends only on the one below it. The scheduler owns state
240
+ transitions; the executor owns retries and cache interaction; the process runner
241
+ owns the subprocess. Rendering and persistence are observers, never participants.
242
+ Read the full design in [docs/architecture.md](docs/architecture.md).
243
+
244
+ ## Documentation
245
+
246
+ | Document | Contents |
247
+ | --- | --- |
248
+ | [docs/architecture.md](docs/architecture.md) | Layers, data flow, state machine, design rules |
249
+ | [docs/configuration.md](docs/configuration.md) | Every `pytaskwirecheck.toml` field |
250
+ | [docs/execution.md](docs/execution.md) | Scheduling, retries, timeouts, cancellation |
251
+ | [docs/caching.md](docs/caching.md) | Fingerprints, storage, integrity, eviction |
252
+ | [docs/cli.md](docs/cli.md) | Commands, flags, exit codes |
253
+ | [docs/python-api.md](docs/python-api.md) | Embedding PyTaskWireCheck as a library |
254
+ | [docs/security.md](docs/security.md) | Trust model and hardening |
255
+ | [docs/development.md](docs/development.md) | Contributing, tests, tooling |
256
+ | [docs/roadmap.md](docs/roadmap.md) | Where PyTaskWireCheck is going |
257
+ | [docs/implementation-status.md](docs/implementation-status.md) | What is done and verified |
258
+
259
+ ## Status
260
+
261
+ PyTaskWireCheck is **0.1.0** — a complete, tested horizontal slice: config → graph →
262
+ scheduler → runner → cache → CLI → TUI → history → notifications. See
263
+ [docs/implementation-status.md](docs/implementation-status.md) for the verified
264
+ breakdown. It is pre-1.0, so the configuration schema is versioned (`version = 1`)
265
+ and unsupported versions are rejected rather than guessed at.
266
+
267
+ ## Contributing
268
+
269
+ Issues and pull requests are welcome. Start with
270
+ [CONTRIBUTING.md](CONTRIBUTING.md) and [docs/development.md](docs/development.md).
271
+ Please follow the [Code of Conduct](CODE_OF_CONDUCT.md) and report
272
+ vulnerabilities via [SECURITY.md](SECURITY.md).
273
+
274
+ ## License
275
+
276
+ MIT — see [LICENSE](LICENSE).