Summary
A persistent /goal loop can spam a gateway channel with repeated near-identical "Goal is complete / I'm stopping" messages when the assistant has reached a terminal answer but the goal judge fails with an API/transport error. The current fail-open behavior treats judge errors as continue, so the goal loop immediately queues another continuation prompt; the agent then repeats the same terminal completion message, and this can happen repeatedly in Discord/Telegram/etc.
This is different from false-positive goal completion (#18421) and explicit-incomplete completion (#26986): here the assistant is explicitly trying to stop, but the judge cannot evaluate and the loop keeps driving more turns.
Observed behavior
In a gateway session, the same completion message was sent repeatedly every few seconds, e.g. variants of:
Goal is complete.
The requested artifact is already done and saved at:
/path/to/generated_artifact.md
I'm stopping here because the requested goal is complete.
The persisted goal state showed that the goal had not been marked done by the judge:
{
"status": "cleared",
"turns_used": 16,
"max_turns": 20,
"last_verdict": "continue",
"last_reason": "judge error: BadRequestError",
"consecutive_parse_failures": 0
}
The assistant messages in the same session contained many repeated terminal responses pointing to the same generated artifact path.
Expected behavior
If the goal judge errors while the assistant's latest response is explicitly terminal (e.g. "Goal is complete", "I'm stopping here", "requested goal is complete"), Hermes should not blindly continue the /goal loop and spam the messaging platform.
At minimum one of these should happen:
- Pause the goal with a user-visible notice such as "goal judge errored; assistant appears to have stopped/completed, pausing to avoid spam".
- Treat terminal completion phrasing as a safe stop condition only when the judge is unavailable.
- Detect repeated near-identical final responses and auto-pause after 1-2 repeats.
- Suppress repeated duplicate gateway deliveries from goal continuations.
Actual behavior
judge_goal() catches exceptions and returns:
return "continue", f"judge error: {type(exc).__name__}", False
Then GoalManager.evaluate_after_turn() keeps the state active and returns should_continue=True as long as the turn budget has not been exhausted. The continuation prompt asks the agent to continue or stop if complete, so the agent repeatedly replies with a terminal completion message, but the judge keeps failing open to continue.
Likely root cause area
hermes_cli/goals.py::judge_goal() exception path
hermes_cli/goals.py::GoalManager.evaluate_after_turn() handling for last_reason like judge error: BadRequestError
- Gateway post-turn goal continuation paths that queue the next turn when
decision["should_continue"] is true
Relevant code shape:
# hermes_cli/goals.py
except Exception as exc:
logger.info("goal judge: API call failed (%s) — falling through to continue", exc)
return "continue", f"judge error: {type(exc).__name__}", False
Suggested fix
Add a spam-safe fallback before returning should_continue=True on judge API errors:
- Detect explicit terminal phrases in
last_response, e.g.:
goal is complete
requested goal is complete
i'm stopping
i am stopping here
stopping because .* complete
- If the judge errored and the response is terminal, set status to
paused (or done, if maintainers prefer) with paused_reason="goal judge error after terminal assistant response".
- Add regression coverage for:
- judge raises
BadRequestError/generic exception;
- assistant response says "Goal is complete... I'm stopping...";
evaluate_after_turn() returns should_continue=False and does not queue another continuation;
- non-terminal responses still fail open to continue as today.
A second defense-in-depth guard could detect repeated near-identical final responses in a goal loop and pause, even if the text does not match the explicit phrase list.
Impact
- Gateway users can receive repeated spam messages in Discord/Telegram/etc.
- The loop consumes model calls until max goal turns are exhausted or the user manually interrupts/clears the goal.
- The behavior is confusing because the assistant says it is stopping, but the outer goal controller keeps restarting it.
Related issues
This issue is the inverse operational failure: assistant response is terminal, but judge errors fail-open to continue and causes repeated terminal-message spam.
Summary
A persistent
/goalloop can spam a gateway channel with repeated near-identical "Goal is complete / I'm stopping" messages when the assistant has reached a terminal answer but the goal judge fails with an API/transport error. The current fail-open behavior treats judge errors ascontinue, so the goal loop immediately queues another continuation prompt; the agent then repeats the same terminal completion message, and this can happen repeatedly in Discord/Telegram/etc.This is different from false-positive goal completion (#18421) and explicit-incomplete completion (#26986): here the assistant is explicitly trying to stop, but the judge cannot evaluate and the loop keeps driving more turns.
Observed behavior
In a gateway session, the same completion message was sent repeatedly every few seconds, e.g. variants of:
The persisted goal state showed that the goal had not been marked done by the judge:
{ "status": "cleared", "turns_used": 16, "max_turns": 20, "last_verdict": "continue", "last_reason": "judge error: BadRequestError", "consecutive_parse_failures": 0 }The assistant messages in the same session contained many repeated terminal responses pointing to the same generated artifact path.
Expected behavior
If the goal judge errors while the assistant's latest response is explicitly terminal (e.g. "Goal is complete", "I'm stopping here", "requested goal is complete"), Hermes should not blindly continue the
/goalloop and spam the messaging platform.At minimum one of these should happen:
Actual behavior
judge_goal()catches exceptions and returns:Then
GoalManager.evaluate_after_turn()keeps the state active and returnsshould_continue=Trueas long as the turn budget has not been exhausted. The continuation prompt asks the agent to continue or stop if complete, so the agent repeatedly replies with a terminal completion message, but the judge keeps failing open tocontinue.Likely root cause area
hermes_cli/goals.py::judge_goal()exception pathhermes_cli/goals.py::GoalManager.evaluate_after_turn()handling forlast_reasonlikejudge error: BadRequestErrordecision["should_continue"]is trueRelevant code shape:
Suggested fix
Add a spam-safe fallback before returning
should_continue=Trueon judge API errors:last_response, e.g.:goal is completerequested goal is completei'm stoppingi am stopping herestopping because .* completepaused(ordone, if maintainers prefer) withpaused_reason="goal judge error after terminal assistant response".BadRequestError/generic exception;evaluate_after_turn()returnsshould_continue=Falseand does not queue another continuation;A second defense-in-depth guard could detect repeated near-identical final responses in a goal loop and pause, even if the text does not match the explicit phrase list.
Impact
Related issues
/goaljudge false positive when assistant claims file created but write failedThis issue is the inverse operational failure: assistant response is terminal, but judge errors fail-open to continue and causes repeated terminal-message spam.