Skip to main content

keepwatch

Keep work alive when the process that started it is not. A 300-line, zero-dependency ledger for agents that run unattended.

ci pypi python


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:

  1. claims the oldest pending task (or --id <id>)
  2. runs the command with inherited stdio, so you still see the output
  3. writes verify ... --rc <real exit code> into the ledger
  4. rc=0 → done; rc!=0 → back to pending for another try
  5. after --max-tries (default 3) failures → left in processing with a suspect record

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.2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for keepwatch 0.2.2
File Size Uploaded Provenance Actions
keepwatch-0.2.2.tar.gz 25.3 kB With provenance Details for keepwatch-0.2.2.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for keepwatch 0.2.2
File Interpreter ABI Platform Actions
keepwatch-0.2.2-py3-none-any.whl Python 3 none any Details for keepwatch-0.2.2-py3-none-any.whl

Total release size: 43.0 kB

Release files / keepwatch-0.2.2.tar.gz

Download URL keepwatch-0.2.2.tar.gz
Size 25.3 kB
Tags Source
SHA-256 checksum
How to use checksums
6d9e11a6ea90ca605b269f5a4115eb5653f4fff99f357f8a2fde304bc3579ca1
BLAKE2b-256 checksum
How to use checksums
2e29a9f2161823a25b2a6bab54253744fb03205f67faa3a7f74c2878cd4d8c76
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 26, 2026.

Transparency log

Release files / keepwatch-0.2.2-py3-none-any.whl

Download URL keepwatch-0.2.2-py3-none-any.whl
Size 17.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
80a782ff59d6985ca59da1d40ee7bb20cf1ed9c2d0673cea2bc720a45b35a9a3
BLAKE2b-256 checksum
How to use checksums
ca6f277a6859a2be8b4b9de532ab3d01d29fc8183843d466701319be0300d1d5
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 26, 2026.

Transparency log

Security for keepwatch 0.2.2

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.

Select from 2 files

Attestation for keepwatch-0.2.2.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 26, 2026.

Transparency log

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.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page