work/caduceus
wip · 2026
caduceus
reviews the prs. does the tickets. one daemon.
a self-hosted rust daemon + hermes plugin. reviews every new pr revision with an isolated worker and publishes one sticky pass/fail verdict. label a ticket autofix and it claims it, runs the harness, and lands the branch + pr.
the story
caduceus is what happens when you stop pretending your ai agent is a chat partner and start treating it like a cron job that writes code. two jobs, one binary: review every pr revision, and turn labeled tickets into pull requests.
the daemon owns everything deterministic: polling, claims, worktrees, timeouts, git, github, retries. the worker is a black box that reads env vars and exits with a code. they never share a library.
that contract is the whole point. you can swap the worker for opencode, codex, claude code, or your own custom script without touching the daemon. the daemon never imports a harness library; the harness never imports a github library.
what it does
- every pr revision gets an isolated review pass and one sticky pass/fail verdict, updated in place as the pr evolves.
- harness-agnostic — the bridge is a small python script you can edit. the daemon doesn’t care.
- worker sandbox is air-gapped: sanitized env, no github credentials ever reach the agent.
- deterministic infrastructure (etag-aware polling, posix flock queue, hard worker timeouts) keeps the plumbing boring on purpose.
- zero inbound networking. refuses to listen on any port. refuses to publish a comment with a hardcoded list of internal tool names.
decisions i’m proud of
▸ two processes, one contract
an llm call is non-deterministic. a state machine is not. the second you let either side reach into the other, you’ve bought a future where a stalled token stream holds your queue hostage. we didn’t buy it.
▸ the worker is a subprocess with env vars
caduceus doesn’t ship an "agent sdk". it ships `worker-bridge.py`. you can read it in a minute. you can replace it in an afternoon. the daemon is unopinionated about which model you call — because the daemon will outlive whichever model is hot this month.
▸ public-voice rule is enforced, not documented
the daemon refuses to post a comment that names internal tools. not because the rule is important in itself — because the rule is a forcing function to keep the agent’s voice something a stranger could read without a decoder ring.