Skip to content
macaddy2Public

About

Automated tasks

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

2 Commits

Folders and files

Repository files navigation

Automated

Build the loop, not the prompt.

A small, dependency-light framework for running Claude (Code) in durable, guard-railed loops — so you can hand it a goal or point it at a recurring job and walk away, instead of hand-holding one prompt at a time.

It's the practical distillation of the article "WTF Is a Loop? Peter Steinberger vs. Boris Cherny":

"You shouldn't be prompting coding agents anymore. You should be designing loops that prompt your agents."

A loop here is cron plus a decision-maker in the body: a model looks at the current state, decides the next action, does it, checks whether it worked, and decides whether to keep going — under hard guardrails, with mandatory self-verification, and durable state in git so work survives a crashed container.


Why this exists (the article in five points)

  1. The loop is the product now, not the prompt. The model got cheap; orchestrating it well is the work — and the cost.
  2. Self-verification is mandatory. "A loop is only as good as its feedback." An open loop with no check is "a machine for generating confident mistakes." → every loop must define a verifier; the framework refuses to run one that doesn't.
  3. Guardrails are mandatory. Max iterations, no-progress detection, and a dollar/token ceiling. "Without guardrails, you get infinite loops and billing surprises orders of magnitude over budget." → every loop must define a budget ceiling.
  4. The reusable unit is a named skill, not a re-derived prompt. Loops call sharp skills (.claude/skills/); a loop with no reusable skills "is just a while-true around a stranger."
  5. Durable state in git so a crashed run resumes where it left off (Gas Town style).

Plus Boris Cherny's five tips, baked in: auto/headless permissions, multi-agent orchestration, nudge-until-done, run in the cloud, and self-verify end-to-end.


Install

pip install -e .            # core (PyYAML only)
pip install -e '.[sdk]'     # add the Claude Agent SDK backend
pip install -e '.[dev]'     # add pytest

Requires the claude CLI on PATH for the cli backend, or the claude-agent-sdk package for the sdk backend.

Send Claude on an errand (the one-liner)

automated errand "fix the failing tests in the payments module and push to a branch" \
  --verify "make test" --budget 5.00

This builds an ephemeral build→test→fix loop, runs it headless (no approval prompts), checkpoints to git each tick, nudges until make test passes or a guardrail stops it, and reports back. Add --save loops/fix-payments.yaml to keep it as a reusable loop, or --dry-run to see exactly what would run without spending a cent.

Run a defined loop

automated validate loops/autonomous-coder.yaml   # schema check (CI-friendly)
automated dry-run  loops/autonomous-coder.yaml   # show exact invocation + run verifier read-only; NO model spend
automated run      loops/autonomous-coder.yaml   # go
automated resume   loops/autonomous-coder.yaml   # continue after a crash / pause
automated resume   loops/pr-babysitter.yaml --approve   # clear a pending human gate (e.g. a merge)
automated list     loops                          # what's available

Zero-Python alternative (bash + claude + jq):

scripts/loop.sh -g "fix the failing tests" -v "make test" -b 5.00 -n 20

The four shipped loops (loops/)

Loop Strategy What it does
autonomous-coder continuous_work Drive a test suite back to green, one root-cause fix per tick.
recurring-poller interval_poller Cron-like watcher — check PRs/inbox/deploys on an interval and act only when needed.
research-pipeline pipeline research → draft → review → output, one stage per tick, judged by an LLM rubric.
pr-babysitter orchestrator The canonical example: babysit PRs, auto-fix CI, address review comments in isolated git worktrees, never merge without a human gate.

How a tick works

restore checkpoint → (in-flight? re-drive) → pre-guardrails (budget, iterations)
   → mark in-flight (+commit) → build prompt → run agent (native budget cap = remaining)
   → self-verify (fail-closed) → update cost/tokens/session → composite progress check
   → post-guardrails (budget, no-progress, human gate) → checkpoint (+commit) → stop or continue
  • Budget is honored across crashes: cumulative spend lives in the checkpoint, and each tick passes the remaining budget as the native --max-budget-usd cap, so a single run is fail-safe even if loop accounting has a bug. (Verified by a test that simulates 20 crashes and asserts spend never exceeds the ceiling.)
  • No-progress is a composite: it trips only when the git tree is unchanged and the verifier's numeric signal hasn't improved and the agent didn't claim progress — for N consecutive ticks. The loop's own state files are excluded so they don't fake progress.
  • Self-verification is fail-closed: a verifier that errors (missing command, crashed test harness, unparseable judge) yields not done, never done.
  • Resume persists the model's session_id so each tick --resumes the agent's own context, and an in_flight_tick marker lets a restart re-drive a tick that died mid-execution.

Config schema (abridged)

name: my-loop
goal: |
  Natural-language goal injected into the agent each tick.
strategy:
  type: continuous_work        # | interval_poller | orchestrator | pipeline
runner:
  backend: cli                 # | sdk | mock
  model: claude-opus-4-8
  permission_mode: dontAsk     # headless default (NOT bypassPermissions)
  allowed_tools: [Read, Edit, "Bash(make test*)"]
verifier:                      # REQUIRED
  type: shell                  # | llm_judge
  command: "make test"
  signal_command: "..."        # prints a number; powers no-progress deltas
guardrails:
  max_iterations: 40
  budget: { usd_ceiling: 10.00 }   # REQUIRED, must be > 0
  no_progress: { ticks: 3 }
  human_gate: { "on": [merge] }
state:
  git: { enabled: true, branch: automated/my-loop, state_path: .automated/my-loop }
  worktrees: false

Safety defaults

  • dontAsk + an explicit tool allowlist is the default — auditable and non-interactive. bypassPermissions is opt-in (runner.dangerous_bypass: true) and refuses to run as root.
  • PR/issue/comment text is untrusted input; the pr-babysitter allowlist excludes force-push, push-to-main, and merge, and routes merges through a mandatory human gate.
  • Per-loop MCP scoping (runner.mcp_servers) keeps tool surface and injection risk small.

Develop

python -m unittest discover -s tests -t .   # 33 tests, all offline (mock runner), no API spend

Architecture: engine.py owns the tick lifecycle; runner/ is the only abstraction over how Claude is invoked (cli / sdk / mock); strategies/, verifiers/, and guardrails/ are small pluggable protocols. Everything is runner-agnostic, so the deterministic MockRunner exercises the entire control flow without a single API call.

About

Automated tasks

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages