HomeBlog › Loop engineering: do/while for AI agents
Blog

Loop engineering: do/while for AI agents

Jul 19, 20267 min readBy the Rysh team

Unattended agent loops need hard ceilings, a graceful landing, and a written definition of done. rysh makes the loop a reviewable file.

My most-used automation is the one that publishes my articles. I write markdown locally; an agent opens Medium in a browser it owns, types the story in, verifies the rendering, publishes, and saves the URL to a results file. I don't watch it do this.

That last sentence is the whole problem with agent loops, isn't it? I don't watch it. Every horror story about autonomous agents starts with someone walking away. So this post is about the part of my stack that makes walking away boring: loop engineering — the discipline of giving a loop hard ceilings, a graceful landing, and a written definition of done.

Budgets you can say out loud

First, vocabulary. rysh counts agent spend the way you count reading:

"This run gets four books" is a sentence you can say in a meeting. "This run gets 800,000 tokens" is not. Every ceiling below uses these units, and they are enforced by the runtime — a run cannot outspend them. (The full case for the vocabulary is its own post: https://rysh.ai/blog/pages-books-shelves.)

The loop is a file

Here is the real recipe, from .rysh/automations/webs/halilagin-medium.md. It's a markdown file with YAML frontmatter: the frontmatter is the control envelope, the body is the task prompt.

{% raw %}

---
description: Publish a local markdown article to medium.com on halilagin's account.
web_profile: halilagin-medium   # persistent, pre-authenticated browser profile
url: https://medium.com/new-story
args: [article_path]            # run as: ##auto web run halilagin-medium /path/to/article.md
output_dir: halilagin-medium/results
loop:
  do:                           # one publishing session
    max_iterations: 150
    max_duration: 15m
    budget:
      size: 4b                  # 4 "books" = 800k tokens, hard ceiling
      watch:
        takeover_when: 90       # at 90% consumed, switch to the wrap-up leg
        takeover_prompt: >
          The publishing budget is used up - stop now. Medium autosaves as you
          type, so the story should exist as a DRAFT: report the draft URL,
          what remains to be done, and save that status to the results file.
          Do NOT publish a half-written article.
  while:                        # the OUTER loop - retry sessions until published
    enabled: true
    max_iterations: 5
    max_duration: 15m
    budget: 20b                 # TOTAL across passes
    prompts:
      until: >
        The results file for this run contains a PUBLISHED story URL for the
        article at {{args}} - the story is live on the profile, not a draft.
      iterate_with: >
        Continue the interrupted publish of {{args}}. Check the profile's
        stories and drafts FIRST: if the draft already exists, open it, finish
        the remaining formatting, and publish THAT draft - never create a
        duplicate story.
---

{% endraw %}

Two loops, two jobs:

Because the loop is a file, the safety envelope is code-reviewable. The until condition shows up in a diff. Your automations live in git, not in a vendor's dashboard.

The takeover leg: land the plane

The two default failure modes of a budgeted loop are both bad: die mid-thought at 100%, or never die because there is no ceiling. takeover_when: 90 adds a third option — at 90% spent, the task prompt loses the wheel and the takeover prompt inherits the reserved 10% to wrap up.

For the publishing recipe, the wrap-up is domain-specific and it matters: do not publish a half-written article — report the autosaved draft URL and what's left. The agent lands the plane instead of hitting a wall. (If your reserved share is too thin to land anything, rysh floors it: a takeover leg always gets at least ~5 minutes / 100 steps / 1 book to finish. The takeover mechanism gets its own deep-dive: https://rysh.ai/blog/takeover-when.)

The budget algebra — and when the engine says no

Here's the part I find genuinely interesting: the while totals and the do values interact through a small, opinionated algebra. Per axis (tokens, wall-clock):

This isn't documentation-ware; you can watch it resolve. For the recipe above:

loop enabled=true  maxIter=5
perPass: tokens=800000  duration=15m0s  takeover=90
outer:   tokensTotal=4000000  duration=0s

The token axis redistributed: 20 books total ÷ 5 passes = 4 books per pass, with the 20-book total held as a hard outer ceiling. The duration axis disabled itself: I set while.max_duration: 15m, but one pass already gets 15m — a total that can't cover even one pass would be a lie, so the engine drops it rather than pretending. If I want a real outer clock, it has to exceed a single pass. Budgets that can say "no" to their author are budgets you can trust.

No goal, no loop

One design rule in rysh that I've come to appreciate the hard way: a loop only arms if until: is written. No flag combination will loop a recipe that lacks a goal — a judge with nothing to judge is just repetition with extra steps. If you take one idea from this post into whatever stack you use, take that one: make "done" a written, reviewable artifact, not a vibe.

Operating the loop

Everything is a first-class command in the terminal:

##auto web run halilagin-medium ./drafts/loop-engineering.md
##auto web status          # live loop table: pass 2/5, state, deadline, last verdict
##auto web stop halilagin-medium     # graceful: in-flight pass finishes, no judge, no iterate
##auto web continue halilagin-medium # resume from the last checkpoint, budget re-armed
##auto web resume halilagin-medium   # fresh budget + latest result seeded into context

And because the browser is CLI-owned and can run headless, the whole thing schedules:

##cron add publish-queue "0 7 * * *" ##auto web run --headless halilagin-medium ./queue/next.md

Log in once with a headed browser (##web headless login halilagin-medium); every headless run reuses the profile. It genuinely works while you sleep — which is only an acceptable sentence because of everything above it.

The honest part

rysh runs on Claude — bring your own Anthropic key — and it's self-hostable: the recipes, the results, and the budget envelopes are files on your infra, not rows in my database. The loop machinery doesn't make an agent capable; it makes an agent bounded. Those are different products, and most stacks only ship the first one.


rysh is in private beta. If you have one workflow you want to run overnight on a leash, that's exactly the conversation we want — we're onboarding design partners: https://rysh.ai/design-partner

Poke holes in the budget algebra in the comments — especially the redistribute/disable policy. That's the part I most want adversarial eyes on.

Related: Pages, books, and shelves: budget your agents like a library · takeover_when: the 90% threshold where a human steps in

Try Rysh

Every pane is a shell and an AI agent — install takes one command.

Get started free →