One Window: Bringing the Loops Back Into Herdr
Two weeks ago I moved my loops out of herdr. This week I moved to the Herdr app and brought them back, as their own kind of agent, beside the Claude Code sessions, the PRs and the on-call queue. It has started to feel like an IDE for agents.
Two weeks ago I moved my hourly loops out of herdr and into automator, a background service with a web dashboard. Last week I taught automator to adopt Claude Code sessions and let them talk to each other.
This week I moved the other way: I switched from herdr in the terminal to the Herdr app, and brought the loops back in.
Lost between two windows
Moving the loops out was the right call. It got them off my screen and gave them a schedule, a budget and a memory. But it split my day across two places.
The sessions I was working in lived in herdr. The loops lived in a browser tab. A loop would ask me a question, and I'd switch to the dashboard, read the run, answer, and switch back. Then I'd lose a minute working out what I'd been doing in the terminal. A session would open a PR, and I'd go to GitHub to see whether review had finished. An alert would fire, and I'd go somewhere else again.
None of these trips was long. But each one dropped what I was holding in my head, and there were dozens a day. I didn't feel busy. I felt a little lost, all day.
What I wanted was one window: the agents, the loops, what they need from me, and the work they're doing, all in the same place.
Why the Herdr app
The Herdr app is herdr redrawn as a native app. It has the same idea (spaces, workspaces, an Agents list that shows who is working and who is idle) and one new thing that mattered to me: mods. A mod is a small script the app draws natively as a panel, a tab or a popup. It can read files, run commands and talk to other mods, and the app reloads it in place every time you save.
Mods can't show HTML, so the dashboard isn't coming with me. That turned out to be a useful constraint. A panel beside the terminal has room for maybe twenty lines, so each one has to answer one question: what needs me here, right now?
Today the right side of my window is a stack of four tabs: Loops, Review, My PRs and On call. The left side lists spaces and agents. The middle is whichever session I'm working in.
Here it is as a low-fidelity wireframe, and it's playable. Click a ↻ loop to open its agent and type to it (Esc stops the run it starts), answer a loop's question (or press Ask… first), switch tabs, and press Address on a PR or Investigate on an alert.
Loops are agents, but not sessions
The first version put a Loops list in the panel, and it was wrong within an hour. The app already has a list of agents, with states, timings and alerts. A second list of things that work on their own was noise.
So now every loop is an agent. automator keeps one terminal per loop in a Loops space, and that terminal tells the app what the loop is doing: working, or blocked on a question or a permission, or failed. When a loop needs me, its row moves into Needs you and the app alerts, the same as when a Claude Code session does. Sessions come first in each group, because they're what I'm working in. Idle loops get their own section under Idle, most recent run first, each row showing the time since its last run. A loop never shows as Done: it is never done, only waiting for its next run.
But a loop is not a Claude Code session, and I didn't want it to pretend to be one. It runs on a schedule, in the background, with a budget and a goal. That difference is in what it does, though, not in how it looks. My first loop view was a reader for the loop's runs, with its own layout and its own commands. It felt like a second tool to learn, and to talk to the loop for real I had to /attach and get a different screen.
So I redrew it as what it is: the loop's own Claude Code session.
- It reads like Claude Code. My messages are
>prompts, the loop's replies are⏺blocks with their Markdown rendered, and a run ends with⎿ done · 4 turns. Tool calls fold into one line, "⏺ Ran 3 shell commands (ctrl+o to expand)", with a green dot once they've all returned and a red one if one failed. While a run works, a spinner sits above the prompt: "✻ Working… (12s · esc to stop)". The last five runs read as one thread, older ones a click away, and a sticky bar names the run I'm reading. - Typing is talking to it. What I type becomes the loop's next message and starts a run, or answers its question if it asked one. If I type during a run, my message queues for the next one, as Claude Code queues a prompt typed mid-turn. Esc stops the run, and the session keeps what it did. There's no
/attachany more: there's no other mode to switch into, because this view is the session. - In the loop's colour. Everything that belongs to the loop (its prompt, its rules, the spinner, its bullets and the sticky bar) is blue where Claude Code is orange. A glance tells me whether I'm typing to a session or to a loop, even though the two now read the same way. Under the prompt, a footer shows the loop's state, its next run and today's spend.
- Its own bill. If I take a loop's session over and work in it by hand, what I spend there is mine, not the loop's. Before, the loop's next run would quietly book my interactive spend against its own budget.
The loop's context, beside the loop
When a loop's agent has focus, the Loops tab shows that loop's context: the pages it published and the PRs it opened or acted on, with their current state. I used to open the dashboard just to answer "what has this loop actually produced?" Now it's on screen while I read its runs. A focused Claude Code session gets the same card, read from its own transcript, so I can see what a session has made without scrolling back through it.
When nothing has focus, the tab shows what needs me across all loops, and below it, updates.
- Needs you holds questions, permission requests, failed runs and budget trouble. Each one has a button that acts on it: answer, pick an option, run the action, dismiss, or open the loop's agent.
- Updates are the news: a loop finished something I'd want to know about, and nothing is blocked. They fold away separately, so news never hides a question.
- Ask about this. Sometimes I can't answer a question without understanding it first. Ask opens an aside: a fresh, tool-less Claude call that sees the item, the loop's goal and the end of the run that raised it. I can go back and forth with it, and nothing reaches the loop unless I put it in my answer. Before this, my only way to understand a question was to talk to the loop itself, which spends its context and its budget on my confusion.
The app already alerts when a loop's agent goes blocked, so the panel only pops up items that no agent speaks for. One alert per thing, not two.
Review, PRs and on-call, in the same window
The loops were only one of my reasons to leave the terminal. The others were the review loop, my PRs and the on-call queue. Each one is now a tab.
Review follows whichever worktree I'm in, and shows where its PR is in review. A PR here goes through two loops. The local one is a review gate that runs before every push and records its rounds: what it found, how confident it was, and how much effort it chose. The remote one is the PR's review rounds: each reviewer's verdict, their suggestions, open threads and the screenshots. The two used to be in a terminal log and on GitHub. Side by side, I can see at a glance whether a warning the gate raised locally was ever answered on the PR.
My PRs lists every PR I have open, grouped by what it needs next: Needs you (a conflict, failing checks, review threads waiting on me), Ready, press merge, and Waiting on review. The important button is Address. It opens a Claude Code session already briefed on that PR's threads and checks, with the job of getting it merge-ready. Clicking a PR opens it in the Review tab.
On call shows who holds the pager, health by area, and triggered alerts by priority. Each alert has Investigate, which opens a session briefed on that alert, plus buttons for a health sweep and a shift handoff.
These three share a pattern I didn't plan: a panel shows the situation, and its main button starts an agent that already knows the situation. I never write the briefing. The panel already has the facts, so it writes the first message.
Sessions and loops, one gesture apart
The last piece closes the circle from last week's post. When I started this post it was only a design. It was built before I finished writing.
Last week's adoption turned a session into a loop, but only from the dashboard, and the way back ("Give back") only worked for adopted loops. And it had a bug: after giving a session back, adopting it again was refused, because the old loop still claimed it. So there was a round trip on paper, but in practice you could only walk one way, from a browser tab.
Now it's one gesture each way, from wherever I am:
- Session to loop:
/to-loop. Inside any Claude Code session, the session drafts its own loop: name, goal, instructions, schedule. It holds the context, so it writes a better job description than any outside summary of its transcript. I confirm, and about a second later automator has the session. I don't even exit: automator ends the idle Claude Code itself, and the same tab turns into the loop's agent. The conversation simply continues as a loop. - Loop to session: Take over. Typing
/takeoverin a loop's view means it's my session now: the same tab turns back into Claude Code, the loop is archived with its settings, and whatever was waiting for it (messages, open questions, the last run's summary) is handed to the session when it opens. Just talking to a loop doesn't need this. I type in its view and it answers in a run, with automator still the only writer. - Back again: Hand back. When I exit a taken-over session, the tab asks whether to hand it back. Enter hands it back as saved. If the session suggests changes, such as a sharper goal, they show up beside the saved settings for me to accept. A session can suggest its goal or schedule, but never its triggers, permissions or working directory. It shouldn't be able to widen its own reach by passing through my hands.
The Loops tab has the same three gestures as buttons: Make this a loop on a focused session, Take over on a focused loop, and Hand back on a session that used to be one. A mod can't open a dialog, so a second click is the confirmation.
Walk the round trip yourself: turn a session into a loop, message it in its view, take it over, then hand it back with or without its suggestions.
The rule underneath all of it is the same one from last week: only one process writes a session at a time. Every transition is a handoff. The current holder lets go before the next one takes over, and nothing is released until the next owner exists. To make that work outside herdr too, automator now learns which sessions are open from Claude Code's own session hooks, so it doesn't depend on any one multiplexer.
What building it taught me
The design was right about the shape and wrong about some details, and using it found most of them.
- One tab, both ways. The first build opened the loop's agent in the Loops space, next to the others. It worked, and it felt wrong: the session I'd been talking to vanished from where I'd left it. Now the tab is a session, then the loop, then the session again.
- A visit was one mode too many. The design had three ways into a loop's session: read it, visit it (
/attach, which paused the loop's runs while I was in) and take it over. Once the loop's view read like Claude Code, the visit had nothing left to offer: typing in the view already reaches the session, and the loop never has to stop for me. Now there are two: talk to it, or take it. - Hand back as it was. In the first live round trip, handing back a loop that had been switched off before I took it over switched it on, and its welcome-back message started a run straight away. Restoring means restoring, including "off".
- Don't trust a number you didn't read.
/to-loopcompacted sessions that were nowhere near full. An interactive session doesn't report its context window, so automator assumed the small default and compacted anything past 80k tokens, which is 8% of the real window. It now reads the window from the loops' own runs. - Never press Ctrl-C forever. To take a session from a terminal, automator ends the idle Claude Code in it. If that doesn't work after three tries, it stops and asks me to exit, rather than risk clearing a message I'm halfway through typing.
- Clicks cost me copy. To make folded tool calls clickable, the view tracks the mouse, and that took away the terminal's own drag-to-select. My first complaint was "I cannot copy from the text." So the view now selects for itself, as Claude Code's fullscreen mode does: drag to highlight, and the text is copied when you let go.
Where this is
Built, and in daily use: loops as agents, the loop's view as its own Claude Code session, the Loops tab with Needs you, updates, Ask and the focused loop's or session's context, the Review, My PRs and On call tabs, and the round trip between sessions and loops.
The trips haven't gone to zero. The dashboard is still where I edit a loop's settings or read its spend over a week. But a normal hour now happens in one window: a loop asks, I answer it beside the code; a PR gets a review, I press Address; an alert fires, I press Investigate; a session turns into something that should keep going on its own, I type /to-loop. Every one of those used to be a context switch.
I didn't expect the biggest change to be how it feels. Two windows made me a dispatcher, always checking somewhere else. One window lets me focus on the session in the middle and trust the edges to tell me when something needs me.
If you enjoyed it, you can buy me a coffee ☕.