keepwatch
Keep work alive when the process that started it is not. A 300-line, zero-dependency ledger for agents that run unattended.
The problem
An unattended pipeline does not fail loudly. It goes quiet:
- a task sits in
pending/and nobody claims it - a worker dies mid-task and the task is stuck in
processing/forever - a worker finishes but never marks it done, so the next round re-dispatches it
- the agent says "all tests pass" and you have nothing to check that against
All four look identical from the outside: nothing happened, and the logs are clean.
What it does
Three folders, one ledger, four verbs.
<root>/agents/<agent>/pending/ tasks a worker can pick up
<root>/agents/<agent>/processing/ tasks someone is holding (with a lease)
<root>/agents/<agent>/done/ closed tasks, never deleted
<root>/audit/YYYY-MM.jsonl append-only record of everything
| Verb | Meaning |
|---|---|
check |
what can I pick up |
claim |
take one, and hold a lease on it |
done |
close it — only if a passing verify record exists |
verify |
record a check result (--rc 0) against a task |
run |
claim → run a command → record its exit code → close or release |
doctor |
deterministic self-check, exit 0 when healthy |
checkpoint |
write a resume note: what is stuck, what to run next |
pipeline |
open or close the gate for new work (exit 14 when closed) |
Plus send, renew, release, reap, status.
The one command you actually need
keepwatch run --who builder -- python -m pytest
That does the whole loop in one go:
- claims the oldest pending task (or
--id <id>) - runs the command with inherited stdio, so you still see the output
- writes
verify ... --rc <real exit code>into the ledger rc=0→done;rc!=0→ back topendingfor another try- after
--max-tries(default 3) failures → left inprocessingwith asuspectrecord
If the process is killed mid-command, nothing is lost: the lease expires and reap puts the
task back. You do not have to remember to write the ledger — the wrapper does it.
Why it matters
claim starts a lease. If the worker dies, the lease expires and reap puts the task
back with a trail (tries, prev_owners). No task is ever stranded, and no task is
silently done twice.
done refuses to close a task unless the ledger holds a verify ... rc=0 for it.
That is the whole point: the model can say "it passed"; the ledger is what proves it.
$ keepwatch done --who builder --id 20261023T083729-8c4fae
keepwatch: refusing to close 20261023T083729-8c4fae: no verify record with rc=0.
Run `verify --id ... --name <check> --rc 0` first, or pass --force.
$ echo $?
13
Install
Python 3.9+, no dependencies.
pip install keepwatch
From a checkout instead:
pip install -e .
PYTHONPATH=src python -m keepwatch.cli --help
60-second demo
keepwatch --root /tmp/ns init builder reviewer
keepwatch --root /tmp/ns send \
--from reviewer --to builder --subject "batch 3: add login page"
keepwatch --root /tmp/ns check --who builder
keepwatch --root /tmp/ns claim --who builder --id <id> --ttl 60
Now pretend the worker dies. Wait for the lease, then:
$ keepwatch --root /tmp/ns status
root: /tmp/ns
builder pending 0 processing 1 done 0 hb 61s (alive)
reviewer pending 0 processing 0 done 0 never seen (unknown)
next:
- builder: 20261023T083750-ef2c5d held by builder, 1s past its lease - run `reap`
$ keepwatch --root /tmp/ns reap
reaped 1 task(s)
20261023T083750-ef2c5d back to pending (tries=1)
$ keepwatch --root /tmp/ns claim --who builder --id 20261023T083750-ef2c5d
claimed 20261023T083750-ef2c5d (try 2) lease until 2026-10-23T08:42:31Z
The task came back with tries=1 and the previous owner recorded. Close it, but only
after proof:
keepwatch --root /tmp/ns verify --id <id> --name pytest --rc 0
keepwatch --root /tmp/ns done --who builder --id <id> --note "e2e green"
Exit codes
Exit codes are the interface; agents read them instead of parsing prose.
| Code | Meaning |
|---|---|
| 0 | ok |
| 1 | bad arguments, IO error |
| 2 | task not found / already taken |
| 3 | task held by another worker (or busy) |
| 13 | blocked: no passing verify, or status found something that needs attention |
Design notes
Files are the source of truth. No database, no daemon, no network. ls and cat
are the debugging tools.
Exclusive create is the mutex, not rename. On at least one Windows filesystem two
concurrent os.replace() calls on the same source both report success. Claiming is
therefore guarded by O_CREAT|O_EXCL on a short-lived token file. This is documented in
docs/PROTOCOL.md because it cost a real bug to find.
One worker per agent box. Leases are per agent directory, so builder running two
processes is a configuration error, not a supported mode.
status exits 13 when something needs attention — so keepwatch status || alert
works in cron.
What it is not
- not a mailbox (see docs/COMPETITION.md for what exists)
- not an orchestrator: it never decides what to do next
- not an agent: it makes no model calls
- not durable execution: it will not replay your side effects
Docs
| File | Contents |
|---|---|
| docs/PROTOCOL.md | layout, task format, lease rules, exit codes |
| docs/COMPETITION.md | why this exists next to agent-mailbox / A2A / Temporal |
| docs/ROADMAP.md | v0.1 → v1.0 and the release checklist |
| docs/zh/ | 中文原文 (Chinese originals) |
Tests: PYTHONPATH=src python -m unittest discover -s tests -v
License
MIT. See LICENSE.
Metadata
Release files for keepwatch 0.2.4
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 | Actions |
|---|---|---|---|---|
| keepwatch-0.2.4.tar.gz | 27.4 kB | With provenance | Details for keepwatch-0.2.4.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| keepwatch-0.2.4-py3-none-any.whl | Python 3 | none | any | Details for keepwatch-0.2.4-py3-none-any.whl |
Total release size: 46.4 kB
Release files / keepwatch-0.2.4.tar.gz
| Download URL | keepwatch-0.2.4.tar.gz |
|---|---|
| Size | 27.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
721db082d61f688b204e685b1a1b2be3fdee925ee5348c30ed54d09edfd7f40b
|
|
BLAKE2b-256 checksum How to use checksums |
f8990f44d2ffdad3aa6b38c85882c025d77275625c420a773b94667bc0972163
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
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.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 29, 2026.
Transparency logRelease files / keepwatch-0.2.4-py3-none-any.whl
| Download URL | keepwatch-0.2.4-py3-none-any.whl |
|---|---|
| Size | 18.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
f24c0031d99f351df5646dcf1aec4c9c1db3912ea7a8842c294b8cb3b9e6f778
|
|
BLAKE2b-256 checksum How to use checksums |
bd52d7d8b0594dd63c03efa64bf724f6cbc8f9d312a80d2ebedd87e9be9b478f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
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.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 29, 2026.
Transparency logSecurity for keepwatch 0.2.4
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.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for keepwatch-0.2.4.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 29, 2026.
Transparency logA 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.