PyTaskWireCheck 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. pytaskwirecheck/__init__.py +32 -0
  2. pytaskwirecheck/api.py +314 -0
  3. pytaskwirecheck/cache/__init__.py +0 -0
  4. pytaskwirecheck/cache/fingerprint.py +176 -0
  5. pytaskwirecheck/cache/policy.py +49 -0
  6. pytaskwirecheck/cache/store.py +367 -0
  7. pytaskwirecheck/cli.py +690 -0
  8. pytaskwirecheck/config/__init__.py +0 -0
  9. pytaskwirecheck/config/loader.py +72 -0
  10. pytaskwirecheck/config/schema.py +170 -0
  11. pytaskwirecheck/config/validation.py +590 -0
  12. pytaskwirecheck/diagnostics/__init__.py +31 -0
  13. pytaskwirecheck/diagnostics/errors.py +86 -0
  14. pytaskwirecheck/engine/__init__.py +0 -0
  15. pytaskwirecheck/engine/cancellation.py +38 -0
  16. pytaskwirecheck/engine/events.py +93 -0
  17. pytaskwirecheck/engine/executor.py +301 -0
  18. pytaskwirecheck/engine/scheduler.py +312 -0
  19. pytaskwirecheck/engine/state.py +123 -0
  20. pytaskwirecheck/graph/__init__.py +0 -0
  21. pytaskwirecheck/graph/builder.py +33 -0
  22. pytaskwirecheck/graph/model.py +104 -0
  23. pytaskwirecheck/graph/planner.py +155 -0
  24. pytaskwirecheck/graph/validator.py +92 -0
  25. pytaskwirecheck/history/__init__.py +0 -0
  26. pytaskwirecheck/history/store.py +164 -0
  27. pytaskwirecheck/logging/__init__.py +0 -0
  28. pytaskwirecheck/logging/artifacts.py +95 -0
  29. pytaskwirecheck/logging/structured.py +83 -0
  30. pytaskwirecheck/notifications/__init__.py +0 -0
  31. pytaskwirecheck/notifications/base.py +42 -0
  32. pytaskwirecheck/notifications/webhook.py +71 -0
  33. pytaskwirecheck/processes/__init__.py +0 -0
  34. pytaskwirecheck/processes/runner.py +265 -0
  35. pytaskwirecheck/processes/signals.py +65 -0
  36. pytaskwirecheck/py.typed +0 -0
  37. pytaskwirecheck/tui/__init__.py +0 -0
  38. pytaskwirecheck/tui/app.py +204 -0
  39. pytaskwirecheck/tui/renderer.py +143 -0
  40. pytaskwirecheck-0.1.0.dist-info/METADATA +316 -0
  41. pytaskwirecheck-0.1.0.dist-info/RECORD +45 -0
  42. pytaskwirecheck-0.1.0.dist-info/WHEEL +5 -0
  43. pytaskwirecheck-0.1.0.dist-info/entry_points.txt +2 -0
  44. pytaskwirecheck-0.1.0.dist-info/licenses/LICENSE +21 -0
  45. pytaskwirecheck-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,143 @@
1
+ """Plain-text console reporting.
2
+
3
+ This renderer provides a useful non-TTY fallback and a CI-friendly mode. It
4
+ never assumes ANSI colour support and keeps concurrent task output labelled.
5
+ It only observes events; it never mutates run state.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import sys
11
+ from typing import Dict, Optional, TextIO
12
+
13
+ from pytaskwirecheck.engine.events import (
14
+ Event,
15
+ RunFinished,
16
+ RunStarted,
17
+ TaskAttemptFinished,
18
+ TaskAttemptStarted,
19
+ TaskOutput,
20
+ TaskStateChanged,
21
+ )
22
+ from pytaskwirecheck.engine.state import TaskStatus
23
+
24
+ _STATUS_GLYPH = {
25
+ TaskStatus.SUCCEEDED: "ok",
26
+ TaskStatus.FAILED: "fail",
27
+ TaskStatus.CACHED: "cached",
28
+ TaskStatus.SKIPPED: "skip",
29
+ TaskStatus.CANCELLED: "cancel",
30
+ TaskStatus.TIMED_OUT: "timeout",
31
+ TaskStatus.BLOCKED: "blocked",
32
+ }
33
+
34
+ _COLORS = {
35
+ TaskStatus.SUCCEEDED: "\033[32m",
36
+ TaskStatus.CACHED: "\033[36m",
37
+ TaskStatus.FAILED: "\033[31m",
38
+ TaskStatus.TIMED_OUT: "\033[31m",
39
+ TaskStatus.BLOCKED: "\033[31m",
40
+ TaskStatus.CANCELLED: "\033[33m",
41
+ }
42
+ _RESET = "\033[0m"
43
+
44
+
45
+ class PlainReporter:
46
+ """A synchronous, line-oriented event reporter."""
47
+
48
+ def __init__(
49
+ self,
50
+ stream: Optional[TextIO] = None,
51
+ *,
52
+ color: bool = False,
53
+ quiet: bool = False,
54
+ show_output: bool = True,
55
+ ) -> None:
56
+ self._stream = stream if stream is not None else sys.stdout
57
+ self._color = color
58
+ self._quiet = quiet
59
+ self._show_output = show_output
60
+ self._buffers: Dict[tuple, str] = {}
61
+
62
+ def _write(self, text: str) -> None:
63
+ try:
64
+ self._stream.write(text)
65
+ self._stream.flush()
66
+ except (BrokenPipeError, ValueError):
67
+ pass
68
+
69
+ def _paint(self, status: TaskStatus, text: str) -> str:
70
+ if not self._color:
71
+ return text
72
+ color = _COLORS.get(status)
73
+ return "%s%s%s" % (color, text, _RESET) if color else text
74
+
75
+ def handle(self, event: Event) -> None:
76
+ if isinstance(event, RunStarted):
77
+ if not self._quiet:
78
+ self._write("Run %s: %d task(s)\n" % (event.run_id, event.total_tasks))
79
+ return
80
+
81
+ if isinstance(event, TaskAttemptStarted):
82
+ if event.max_attempts > 1 and not self._quiet:
83
+ self._write(
84
+ " %s: attempt %d/%d\n"
85
+ % (event.task_id, event.attempt, event.max_attempts)
86
+ )
87
+ return
88
+
89
+ if isinstance(event, TaskAttemptFinished):
90
+ return
91
+
92
+ if isinstance(event, TaskStateChanged):
93
+ self._on_state_changed(event)
94
+ return
95
+
96
+ if isinstance(event, TaskOutput):
97
+ self._on_output(event)
98
+ return
99
+
100
+ if isinstance(event, RunFinished):
101
+ self._on_run_finished(event)
102
+ return
103
+
104
+ def _on_state_changed(self, event: TaskStateChanged) -> None:
105
+ if event.status is TaskStatus.RUNNING:
106
+ if not self._quiet and not self._show_output:
107
+ self._write(" %s: running\n" % event.task_id)
108
+ return
109
+ if not event.status.is_terminal:
110
+ return
111
+ glyph = _STATUS_GLYPH.get(event.status, event.status.value)
112
+ suffix = ""
113
+ if event.status is TaskStatus.FAILED and event.message:
114
+ suffix = " (%s)" % event.message
115
+ elif event.status in (TaskStatus.TIMED_OUT, TaskStatus.BLOCKED):
116
+ if event.message:
117
+ suffix = " (%s)" % event.message
118
+ label = self._paint(event.status, glyph)
119
+ self._write(" [%s] %s%s\n" % (label, event.task_id, suffix))
120
+
121
+ def _on_output(self, event: TaskOutput) -> None:
122
+ if not self._show_output or self._quiet:
123
+ return
124
+ key = (event.task_id, event.stream)
125
+ buffer = self._buffers.get(key, "")
126
+ buffer += event.data.decode("utf-8", errors="replace")
127
+ while "\n" in buffer:
128
+ line, buffer = buffer.split("\n", 1)
129
+ prefix = event.task_id
130
+ if event.stream == "stderr":
131
+ prefix += "!stderr"
132
+ self._write(" %s | %s\n" % (prefix, line))
133
+ self._buffers[key] = buffer
134
+
135
+ def _on_run_finished(self, event: RunFinished) -> None:
136
+ summary = event.summary or {}
137
+ counts = summary.get("counts", {})
138
+ duration = summary.get("duration_seconds", 0.0)
139
+ status = "succeeded" if event.succeeded else "failed"
140
+ rendered = ", ".join("%d %s" % (counts[k], k) for k in sorted(counts))
141
+ self._write(
142
+ "Run %s %s in %.2fs (%s)\n" % (event.run_id, status, duration, rendered)
143
+ )
@@ -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,45 @@
1
+ pytaskwirecheck/__init__.py,sha256=9icSd3bik9pcpTr9OAkfMYBeuX4hr8h5rqb6Td93H0Q,804
2
+ pytaskwirecheck/api.py,sha256=HTITnLIH2aJJKITPfwoA7JWj-hfULTo3eRXMb7dJvZ8,11073
3
+ pytaskwirecheck/cli.py,sha256=3b6NCrrjgkj1Wjyu8JE5j_OP5oiv5V0gviRuzvLyego,22618
4
+ pytaskwirecheck/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
+ pytaskwirecheck/cache/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
+ pytaskwirecheck/cache/fingerprint.py,sha256=57-6HLsbtbfi4CG16X_E8OO9W9_pK8F0fICtwwREtyM,5588
7
+ pytaskwirecheck/cache/policy.py,sha256=cj4t3A-uONAEmTreZF1L77REtrJu0WXDRqJUJwpKZOY,1527
8
+ pytaskwirecheck/cache/store.py,sha256=VT_N_V6Xp-wHkfK4HBpmOK8N865ieghCFCmZJAXR6NA,12581
9
+ pytaskwirecheck/config/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
+ pytaskwirecheck/config/loader.py,sha256=cFn05YrmVkcSKUVrj0vBVR-zAK6WFiLpAA4UMKtP7Bs,2569
11
+ pytaskwirecheck/config/schema.py,sha256=POJHgX-_1gt_q5ZaEiE7mYye7F2Y3HQNGB7UhApF2RU,4806
12
+ pytaskwirecheck/config/validation.py,sha256=PG_NX5OjJQVcyL18Sh-I8tsfp3S40qo7DBHI-Hvh7QU,18630
13
+ pytaskwirecheck/diagnostics/__init__.py,sha256=eOYVOYF_FiCi1Au3rclpU0xlImzxv1x4j52kmkxzDLA,643
14
+ pytaskwirecheck/diagnostics/errors.py,sha256=THR3OJmXmuiP4hkFGDIh1vH09KIrCi-haVCWVnKSBew,2519
15
+ pytaskwirecheck/engine/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
16
+ pytaskwirecheck/engine/cancellation.py,sha256=1tXIWN31p_mNUbn65_RW2iNVoz07o04ijseTlLXuUec,1046
17
+ pytaskwirecheck/engine/events.py,sha256=5-LD2uFEebvF5qhK7Gqx1wsX974P9Ay6OX7Wa8c_eT8,2014
18
+ pytaskwirecheck/engine/executor.py,sha256=fBv89FUUCQg-yvjxOm_4XKhGUS-5RU3q6zobUmhbndM,10590
19
+ pytaskwirecheck/engine/scheduler.py,sha256=T_PkmwbuNtL74E-dQjPcJkPkVr23TImS3NRILhOmouU,11109
20
+ pytaskwirecheck/engine/state.py,sha256=TdKCpegMIjNuFwa4P2YxJjdZejyMClI2258Q4zAsXZw,3226
21
+ pytaskwirecheck/graph/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
22
+ pytaskwirecheck/graph/builder.py,sha256=GhlrhjUpyS03B2CuEkK9qBM1OBwKP8yl1lIZ8DEN0rg,1178
23
+ pytaskwirecheck/graph/model.py,sha256=-FXJZclPFUw2XNrJ43g1iMPRKI8II0aCfp2DodumEoI,3334
24
+ pytaskwirecheck/graph/planner.py,sha256=aUbUE72Y2qzKoyszqiWTjptZnlAXBFk8b88N01QUECk,5068
25
+ pytaskwirecheck/graph/validator.py,sha256=PbEfPr3QEboKfgyEpixOfRHrGKla2VZz-jqIDfCDtG8,3054
26
+ pytaskwirecheck/history/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
27
+ pytaskwirecheck/history/store.py,sha256=6heaoKi7E8BulUsUVUejeeT_31bxREptmeGwtrzPhRI,5530
28
+ pytaskwirecheck/logging/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
29
+ pytaskwirecheck/logging/artifacts.py,sha256=onpRAxO2DAYzhK6s7DepzJ_klbSZxqLKuDdGAou5uS0,2935
30
+ pytaskwirecheck/logging/structured.py,sha256=y20JoU6EVoMbMFSmq-ykyYpCr9F6DxHaD4Lc5ZiWa0Q,2510
31
+ pytaskwirecheck/notifications/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
32
+ pytaskwirecheck/notifications/base.py,sha256=0I-fMlKBcKYRoFuSAk3MjUkx1Foo93xT9DvcpFLiqKI,1262
33
+ pytaskwirecheck/notifications/webhook.py,sha256=dfkIa0H0OSSqLV4Cp4J7ohwaLEP-3lXSny3gHJDtq5o,2544
34
+ pytaskwirecheck/processes/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
35
+ pytaskwirecheck/processes/runner.py,sha256=HuhABwnIujW1eB9eDv4fjmCopSCSJSTd6D5QAnY2E4Y,8149
36
+ pytaskwirecheck/processes/signals.py,sha256=o6IeQnxqx2ZEBeZdLYCfxGFmnZTbJNocbb_2eZHnSok,1923
37
+ pytaskwirecheck/tui/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
38
+ pytaskwirecheck/tui/app.py,sha256=_EBYVEwh5JiR_xC_AYXCukcglu85tSH01yd9ZSQJbUg,6780
39
+ pytaskwirecheck/tui/renderer.py,sha256=_eSpNJYCePqbadCozlqQjJ5_541ttJDHSpHTvRyH3jw,4652
40
+ pytaskwirecheck-0.1.0.dist-info/licenses/LICENSE,sha256=7AYaQADXpsZdOBzCUVw82nbd6I89xeX0EEp9TrpfZLk,1085
41
+ pytaskwirecheck-0.1.0.dist-info/METADATA,sha256=47U65AHJ3pjspucfuhBUq-yityS0X_ehJ28C3zNQQPA,12237
42
+ pytaskwirecheck-0.1.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
43
+ pytaskwirecheck-0.1.0.dist-info/entry_points.txt,sha256=z3oOm1Vi2rpmbKL9OHBwdsTzte7k18Gtxleuz-PiaLU,61
44
+ pytaskwirecheck-0.1.0.dist-info/top_level.txt,sha256=gR0zDJHu-PA-6EkkhyxtWPHsAZcXs7pDohOkOaX6JAI,16
45
+ pytaskwirecheck-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (82.0.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ pytaskwirecheck = pytaskwirecheck.cli:main
@@ -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 @@
1
+ pytaskwirecheck