BlogBy Panashe

Guardrails that keep work moving

A guardrail that only halts work makes more work for me, so each of mine tries to send a task somewhere useful before it gives up on it.

When I first wrote down rules for my agents, each rule was a wall. If a task broke the rule, the run stopped. That felt safe, and it also meant I came back to a pile of stopped runs and a list of questions. A guard that only says no turns me into the person who has to say yes, which is the exact job I built Murongi to avoid.

So I changed the design question. Instead of asking what should stop a run, I ask where the work should go when it cannot proceed here. Most of the time there is an answer.

The five guardrails

Murongi has five, and I think of them as rings, with the narrowest on the inside:

  • Claimed. One agent takes each task, and the claim is visible to the others.
  • Reviewed. A second agent checks each change before it lands.
  • Scoped. An agent gets only the access its task needs.
  • Limited. A run that goes past its budget is stopped, and one switch halts new work.
  • Human in the loop. Spending beyond a budget, publishing and deleting wait for me.

Read quickly, that sounds like five ways of stopping things. In practice the first four mostly decide who does what, and where.

Routing instead of halting

Take the claim as a first example. Before I had it, two agents sometimes started the same fix. The old response would have been to kill the later one. Now the second agent looks at the queue, sees the task is claimed, and picks a different one. Little is wasted, because the guard answered with another task.

Take a task that runs over its limit. Say a run has used far more of its allowance than a job of that kind usually needs. Halting it and filing a complaint is one option. What I prefer is that it goes back to the queue with a note about how far it got, so that another agent, or the same one on a machine with more room, can continue from there. The work that was done is kept, and the budget is still respected.

Consider a machine that is busy or asleep. A task waiting for it does not sit there. It can move to another machine that is free, or a message can wake the sleeping one. The guard here is a check that the target can take the work, and the response to a failed check is a different target.

Review follows the same shape as the others. When the second agent finds a problem, the change does not die. It goes back to the first agent with the findings attached, and the cycle runs again. A failed review sends the change around the loop again.

Where stopping is the right answer

Some things should halt, and I keep those few. The stop switch ends all new work across the machines, because sometimes I just need quiet while I look at something. Anything that spends beyond a budget I set, publishes, or deletes waits for a person. These are the cases where a wrong guess costs more than the delay, and I would rather be asked.

Even then I try to make the pause cheap. The question arrives with the context already written, and I can answer it from my phone. The run waits for me without holding up the rest of the queue, so other tasks keep moving while one sits at the gate.

What I have learned from it

A good guard has two jobs. It has to prevent the bad outcome, and it has to leave the system in a state where useful work can continue. I used to count only the first. Now I judge each rule by both, and when a rule does the first without the second, I look for the place where the task can go instead.

I try to be clear about what these rules do and do not give me. They reduce the number of mistakes that reach my repositories. They do not remove the need for me to read what lands. If you want the background on how the whole loop fits together, the first post covers it, and the next one is about the meter that feeds the Limited guardrail.