Winslow
A workflow and state manager that serves terminals and agents.
Winslow runs work as a set of small tasks with declared dependencies. You drive them from where you are:
- A live terminal dashboard on your machine,
winslow run - A terminal connected to a shared host,
winslow connect - An agent over MCP,
winslow serve --endpoints ws mcp - Headless in cron or CI,
winslow run --mode headless
Every task knows two things: how to do its work, and how to tell whether that work is already done. Winslow checks the second before it does the first, so a second run of a workflow only does what still needs doing.
If you have a pile of scripts held together with "did this step already run?" checks, Winslow is that pattern as a framework.
Demo
https://github.com/user-attachments/assets/23426158-0447-421b-ab16-caf4c0d9103e
Install
Each mode is one extra:
uv add "winslow[serve]" # the shared host
uv add "winslow[mcp]" # the shared host, with the MCP endpoint
uv add "winslow[tui]" # one machine: the UI and the sessions together
uvx --from "winslow[connect]" winslow connect ws://host:8866 # a terminal, no project needed
Winslow needs Python 3.12 or later. pip install 'winslow[serve]' works the
same way. A headless run in cron or CI needs the bare winslow package only.
Quick start
A workflow is a directory with a Workflow class and the Task classes that
belong to it. The file name workflow.py marks the directory as a workflow
package, and every .py file next to it belongs to that workflow. Put this in
workflows/etl/workflow.py:
import os
from winslow import Workflow, Task
class Etl(Workflow):
pass # The name defaults to "etl", the kebab-cased class name.
class DownloadData(Task):
def run(self):
download("s3://bucket/raw.csv", "/data/raw.csv")
def check(self):
return os.path.exists("/data/raw.csv")
class TransformData(Task):
dependencies = DownloadData
def run(self):
transform("/data/raw.csv", "/data/clean.csv")
def check(self):
return os.path.exists("/data/clean.csv")
Start the terminal UI from that directory:
winslow run
Or serve the directory and connect to it from another terminal:
winslow serve # on the host
uvx --from "winslow[connect]" winslow connect ws://127.0.0.1:8866 # from a terminal
Or run it headless, for cron and CI:
winslow run --mode headless --workflow etl
The getting started page walks through all three. Serve and connect covers a team on one host and an agent over MCP.
The core idea: run and check
Two methods are the whole contract:
run(self)makes a change to the world.check(self)reports whether the wanted end state already holds.
Before it runs a task, Winslow calls check(). If the check passes, the task is
skipped. If it fails, run() executes and Winslow calls check() again to
confirm the result. A workflow is therefore idempotent and resumable: interrupt
it halfway, run it again, and it continues from where it stopped. To force a
rerun, winslow run --force-run skips the pre-check.
A task that only verifies state can omit run(). It becomes a read-only check.
Start as a status board and automate later
A workflow built only from checks changes nothing. It reads your systems and reports what is true. That makes adoption incremental:
- Describe the runbook you already have as checks, one
check()per step, automated or manual. The TUI is now a live status board over your process as it exists today. - Add
run()to one task at a time, wherever automation pays off. The rest stay checks, and the workflow is usable at every step in between.
A task can also automate part of its work and leave the rest to a person. The docs show how.
What else is there
Each of these is optional. Start with run and check and add the rest when
you need it.
-
Dependencies and ordering. Declare
dependencies = OtherTask, a tuple, or a task group name. Winslow builds the dependency graph, detects cycles, and runs the tasks in order. Mark foundational stepsis_premierand cleanup stepsis_terminal. -
Eligibility and guards. Override
is_eligible()to skip a task in some environments, orcan_run()to block it until a precondition holds. A reusable rule is a constraint class:class DeployTask(Task): runnability_constraints = [BusinessHoursOnly]
-
Parameterized tasks. One task class fans out into many instances through
Parameterdeclarations, for example oneProcessRegiontask per region, each tracked and run on its own. -
A filter language you can extend. Narrow the view or a run with expressions like
build,test,!g deploy, or~lint & !group nightly, in the search box of the UI or with--filteron the CLI. SubclassTaskFilterto add a!commandof your own, and it works everywhere filters do. -
Declarative caching. Share expensive data such as station lists, calendars or reference tables through
GlobalCache(process scope) andWorkflowCache(session scope) classes. Declare fields with@entry(lazy, eager,depends_on,ttl). Eager fields load in parallel before the graph is built, andJsonFileStoragekeeps a cache warm across processes. -
Error telemetry. Report task and workflow errors to Sentry (
winslow[sentry]) or OpenTelemetry (winslow[otel]) by setting their environment values. Each error reaches the backends once. -
Live terminal UI. Watch tasks change state, stream per-task logs, browse the execution history, and run or re-check single tasks by hand, all from the dashboard.
-
Serve and connect.
winslow servestarts the engine, the component that owns the sessions, and exposes it over a websocket. Every connected terminal sees the same sessions.--endpoints ws mcpadds an MCP endpoint, so an agent lists, starts, runs and inspects sessions with the same reads and actions a terminal has. -
A pluggable UI. The dashboard and the workflow screen are assembled from plugins that fill named slots. Subclass
UIPluginto add a tab or a panel, or to replace a built-in one. UI plugins and filters live in their own packages, listed in the settings module of the project.
CLI at a glance
winslow serve # the sessions on 127.0.0.1:8866
winslow serve --host 0.0.0.0 --endpoints ws mcp # on the network, with /mcp for agents
winslow connect ws://host:8866 # the TUI against a server
winslow run # the UI and the sessions in one process
winslow run --workflow etl # UI, pre-selecting a workflow
winslow run --mode headless --workflow etl # headless run
winslow run --mode headless --check ... # check completion without running
winslow run --dry-run ... # call dry_run() instead of run()
winslow run --clear-cache ... # invalidate every cache entry first
winslow show # list workflows
winslow show --initialize --workflow etl # initialize one workflow, list its tasks
winslow show --initialize --workflow etl --with-deps # ...with each task's dependencies
Winslow discovers the workflows from the current directory. An Orchestrator
subclass that adds global options is optional.
How it compares
Winslow runs on one host. There is no scheduler daemon and no metadata
database. Done-ness is whatever check() observes right now, a file, a table,
an API response, a merged PR, so when the world drifts, the next run sees it.
Tasks run where the engine runs, on your laptop under winslow run or on the
shared host under winslow serve, and cron or CI triggers the unattended runs.
If you need distributed workers, a built-in scheduler, or a central run history, a different tool fits better. The tool selector compares Winslow with the common alternatives under the same rules. Tick what your situation requires and see what fits.
Trust model
Treat a workflow directory like a Makefile or a conftest.py: running
winslow in a directory runs the code of that directory, imported at startup
before any prompt. A plugin loads when the settings module of the project
lists it, and the plugin guide shows
the lists. A serve credential grants every action on the serving host,
including starting a workflow, which runs its code. The full trust model and
the vulnerability reporting process are in SECURITY.md.
Status
Winslow is early, 0.x, and the API may still change between minor versions. The full documentation lives at winslow-workflow.org.
Questions and feedback
- Questions, use cases and ideas: Discussions
- Reproducible bugs: Issues
Real use cases are welcome while the API is still settling. What you automate, and what fought you, both shape what 0.x becomes.
License
Metadata
Release files for winslow 0.8.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| winslow-0.8.0.tar.gz | 414.6 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| winslow-0.8.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 888.3 kB
Release files / winslow-0.8.0.tar.gz
| Download URL | winslow-0.8.0.tar.gz |
|---|---|
| Size | 414.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
d9eac9d74d7a0d2d833158bcc9fc70d4044e7ed8a3ef8d3f3e2032e7f284de67
|
|
BLAKE2b-256 checksum How to use checksums |
d1cb52c2ae0b082c2a29fddafe75d96e1a7df9913e2a5538329c200bd84223f5
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.13.5 {"installer":{"name":"uv","version":"0.13.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / winslow-0.8.0-py3-none-any.whl
| Download URL | winslow-0.8.0-py3-none-any.whl |
|---|---|
| Size | 473.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
5d5bffe05cb191f81efca7db81e02068148e7106907bee972912f7f99c262a1b
|
|
BLAKE2b-256 checksum How to use checksums |
8673b0ea03cd2e7fdfe037d61de0effbc7a0e8af026df91aa58ef6fbdb4a968a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.13.5 {"installer":{"name":"uv","version":"0.13.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Security for winslow 0.8.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.