Persistent Goals: Let Hermes Work Until It's Done
The /goal command gives Hermes a standing objective that survives across turns. After every turn, a lightweight judge model checks whether the goal is satisfied by the assistant's last response. If it isn't, Hermes automatically feeds a continuation prompt back into the same session and keeps working — until the goal is achieved, you pause or clear it, or the turn budget runs out. It's Hermes' take on the Ralph loop pattern, independently implemented but inspired by the /goal shipped in Codex CLI 0.128.0.
When to Use /goal
Use /goal for tasks where you want Hermes to iterate on its own without re-prompting every turn. Good candidates include "Fix every lint error in src/ and verify ruff check passes," "Port feature X from repo Y, including tests, and get CI green," or "Investigate why session IDs sometimes drift on mid-run compression and write up a report." Tasks where the agent does one turn and stops don't need it; tasks where you'd otherwise have to say "keep going" three times are exactly where this shines.
There's a sharp boundary versus the Kanban multi-agent board. /goal is strictly single-session: the loop feeds continuation prompts back into this conversation until the judge says done. It never creates a kanban card, never assigns work to another profile, and never fans out. If you want work on the board, create it there with hermes kanban create …. (One overlap: a kanban card created with --goal reuses the same continuation engine inside that card's own worker session.)
How the Loop Runs
Set a goal and the loop begins immediately:
/goal Fix every failing test in tests/hermes_cli/
Hermes accepts the goal with a visible turn budget, starts working as if you'd sent it as a normal message, runs the judge after the turn, and fires a continuation if needed — showing ↻ Continuing toward goal (1/20) with the judge's reason. It terminates with either ✓ Goal achieved or ⏸ Goal paused.
The judge model is the heart of the loop. After each turn it receives the standing goal text plus the agent's most recent response and replies with a strict one-line verdict: done, blocked, continue, or wait. It's deliberately conservative — it marks a goal done only when the response explicitly confirms completion. If a goal proves unachievable, the judge returns blocked rather than done, pausing with an explanation so you can re-scope rather than burning budget or having impossible work waved through.
Completion Contracts Make Judging Precise
A bare /goal <text> works, but vague goals produce vague judging. Hermes adapts the idea of a completion contract — an optional structure naming what done means, how to prove it, what not to break, what's in scope, and when to stop. The five optional fields are outcome, verification, constraints, boundaries, and stop_when.
You can let Hermes draft one with /goal draft <text> (recommended) or write it inline with field: value lines:
/goal Migrate auth to JWT
verify: pytest tests/auth passes
constraints: keep the /login response shape unchanged
boundaries: only touch services/auth and its tests
stop when: a DB schema migration is required
With a contract set, the judge decides done only when the verification criterion is met with concrete evidence — a command result, file excerpt, or test output — not a loose "looks done" claim. You can append extra criteria mid-loop with /subgoal <text>, and quality gates add deterministic shell commands that must exit 0 before the goal can complete at all.
Parking, Budgets, and Persistence
Some goals are gated on long-running background processes like CI on a pushed PR. Rather than re-poking for "is it done yet?" busy-work, the judge can return a wait verdict, parking the loop until the process exits or its pattern fires — capped at 30 minutes per wait. /goal status shows ⏳ Goal (parked …) while waiting.
Two safety properties keep the loop sane:
- Fail-open semantics — if the judge errors, Hermes treats it as
continue, so a broken judge never wedges progress. - Turn budget — default 20 continuation turns (
goals.max_turns). When hit, Hermes auto-pauses and tells you to/goal resume(resets the counter) or/goal clear.
Your messages always preempt the loop, and the judge re-runs after your turn. Goal state persists in SessionDB.state_meta, so /resume after a reboot picks up right where you left off. Finally, the continuation prompt is a plain user-role message that doesn't invalidate Hermes' prompt cache — a 20-turn goal costs the same cache-wise as 20 turns of normal conversation. Configure the judge via auxiliary.goal_judge to route it to a cheap, fast model.
Key Takeaways
/goalsets a standing objective that persists across turns until achieved, paused, or budget-exhausted.- A conservative judge model checks completion after every turn and drives a
done/blocked/continue/waitloop. - Completion contracts and inline
verify:/constraints:fields make "done" mechanically checkable. - Fail-open judging and a 20-turn default budget (via
goals.max_turns) keep the loop safe. - Goal state persists across restarts and
/resume, and doesn't invalidate the prompt cache.