APEX Nerd Labs
Back to blog
AI-Automation

A plugin born from a billing error: building loop-contract for Hermes

Christian Brown August 20, 2026
A plugin born from a billing error: building loop-contract for Hermes

A subagent was designing an autonomous coding loop for me. Mid-task, it hit an OpenRouter 402 — out of credits.

Instead of waiting on billing, I wrote the design myself. That frustration turned into something useful.

The problem with unattended agents

An autonomous coding loop has one failure mode that matters: the agent decides it is finished when it is not. Everything else — burning budget, spinning on the same edit, pushing a broken branch — is downstream of that.

The usual mitigation is to ask the model to check its work. That does not help, because the thing being checked and the thing doing the checking are the same unreliable component. The billing error forced me to sit down and build on the opposite premise: completion is a fact established outside the model, or it is not established at all.

What I built instead of waiting

The first version was just a set of rules for one project. Then someone asked if I could make it generic enough to use anywhere, so I rebuilt it as a real plugin. Then the plugin spawned a container version that works with any agent — Hermes, Claude Code, OpenCode, a raw API loop, even curl.

How verification actually works

The core insight: loopcontract_complete_item takes no "did it pass?" argument. It does not trust the agent's claim. Instead, it asks whether a receipt exists for this item, bound to the worktree fingerprint as it is right now, and not expired.

The receipt is minted by loopcontract_verify, which runs your actual shell commands — pytest, mypy, npm run build, whatever you configure — and only issues a receipt on a full passing run.

Situation Result
Verifier never ran rejected: no_receipt
Verifier ran and failed no receipt minted → rejected
Verifier passed, then code changed rejected: stale_fingerprint
Only one component was run no receipt → rejected
Verifier passed, tree unchanged completed

The third row is the one people miss. "Tests passed, then I made one more small change" is exactly how a green run stops describing the shipped code.

Four termination rails

All evaluated before anything the model reports:

  • Iteration cap — hard ceiling on loop turns (default 8)
  • Time budget — wall-clock minutes (default 20)
  • Token budget — cumulative tokens (default 200K)
  • No-progress detection — N consecutive identical worktrees (default 3)

No-progress compares a content fingerprint (HEAD plus the hash of every dirty file), not a diff. Committing advances the fingerprint, so real work is never mistaken for stagnation — while an agent genuinely rewriting the same bytes is still caught.

Human checkpoints

Irreversible actions are blocked in the tool-dispatch path, not merely discouraged in the prompt. git push --force, npm publish, terraform apply, rm -rf, DELETE FROM, and about 20 more patterns are refused before the command runs, whether or not the model thinks to ask.

loop-contract BLOCKED this terminal call: it matches the human-checkpoint
pattern /\bgit\s+push\b/ ...

After you approve, the agent calls loopcontract_checkpoint(action=..., resolve=true) with the exact command string, and that specific action is unblocked. The key: the agent cannot override this. A checkpoint forces a pause.

The container version

The Hermes plugin ran inside the Hermes process and trusted that process to call its hooks. The container version removes that trust entirely: the agent is assumed to ignore every convention.

The enforcement service is HTTP, bound inside the container. A CLI or library would execute in the same filesystem the agent controls, so the agent could forge a STATE.json write or skip the check entirely. An HTTP endpoint is a genuine trust boundary: the agent gets a URL and a mounted worktree, nothing else.

The service is the only thing that can mint a receipt. /verify runs the contract's real shell commands server-side and mints a receipt only on a real exit-code-0 sweep. There is deliberately no field on any endpoint through which the agent asserts success.

Testing it for real

I didn't ship this on faith. The plugin has a 14-test regression suite. Before releasing, I:

  • Ran the whole cycle against a real test repo
  • Confirmed the receipt gate rejects completion without a valid receipt
  • Confirmed a stale fingerprint (code changed after passing) blocks completion
  • Confirmed a blocked git push only unblocks after explicit resolve
  • Dropped the iteration cap to 1 and confirmed it stops at the limit
  • Verified the 3-identical-diffs detection catches real stuck loops

What you can steal

The plugin is open source under MIT: github.com/Apex-Nerd-Labs/loop-contract

Even if you don't use it, the patterns transfer to any agent setup:

  1. Never let the agent decide when it's done. Use a checklist with external verification that mints receipts.
  2. Bind receipts to worktree fingerprints. A passing run that doesn't describe the current code is worthless.
  3. Set hard termination rails. Iteration cap, time budget, token budget, no-progress detection — any one should stop the loop.
  4. Block irreversible actions at dispatch. Don't ask the model to be careful; refuse to run the command.
  5. Trust boundaries matter. If the enforcement logic runs in the same process the agent controls, it's not enforcement.

If you're letting an agent work unattended on real code, these patterns are the difference between "useful tool" and "expensive mistake."

Want this answer for your business?

Book a free 15-minute Mini Audit. I’ll find your single biggest bottleneck and hand you one fix.

Book a free Mini Audit