Skip to content

[Bug] Gateway does not call _post_turn_goal_continuation after each turn, preventing goal evaluation #62202

Description

@Avakiano

The gateway (gateway/run.py) does not invoke _post_turn_goal_continuation() after each assistant turn. As a result, the goal judge (evaluate_after_turn) is never called, turns_used never increments past 0, and the /goal loop is effectively dead despite all configuration being correct.

Reproduction Steps

  1. Set a goal: /goal Respond "hello" and stop.
  2. Wait for the assistant to respond with "hello"
  3. Check status: /goal status → shows 0/20 turns
  4. The goal judge never evaluates the response, turns never increment

Expected Behavior

Per the documentation, after each assistant response the goal judge should fire, evaluate the response against the goal, increment turns_used, and either continue or mark the goal as done.

Root Cause

In gateway/run.py, after the agent produces a response:

_agent_result = await self._handle_message_with_agent(event, source, _quick_key, _run_generation)

The code immediately after (lines ~10272-10288) checks if _final_text.strip(): and then calls self.session_store.get_or_create_session(source) → _post_turn_goal_continuation(...). However, this section is never reached in actual message processing — the function returns before executing the goal continuation hook.

Evidence

The goal system itself works perfectly. Calling evaluate_after_turn() directly from a Python script:

mgr = GoalManager(session_id="<active_session_id>")
result = mgr.evaluate_after_turn(
last_response="Test completed.",
user_initiated=True,
)

→ verdict="done", turns_used=1, status="done", persisted to DB correctly

The auxiliary client resolves correctly:
get_text_auxiliary_client("goal_judge") → OpenAI client
Model: nvidia/nemotron-3-super-120b-a12b
Chat completions: available

The goal judge model responds with valid JSON:
{"done": true, "reason": "Goal completed."}

DB evidence:

  • Querying state_meta for goal:<session_id> shows turns_used=0, last_verdict=None for ALL goals ever created via the gateway
  • After direct evaluate_after_turn() call: turns_used=2, last_verdict="done"

Config Used

goals:
enabled: true
max_turns: 20
allow_early_stop: true
send_result_reply: true

auxiliary:
goal_judge:
provider: nvidia
model: nvidia/nemotron-3-super-120b-a12b
max_tokens: 1024
timeout: 120

Workaround

Until this is fixed, a cron-based watchdog can replicate the goal behavior:

Runs every 60s, detects new assistant messages, calls evaluate_after_turn

Only notifies on goal completion

See: /data/.hermes/scripts/goal_watchdog.py

Install: hermes cron create --no-agent --script goal_watchdog.py --schedule "every 1m"

Additional Context

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Medium — degraded but workaround existscomp/gatewayGateway runner, session dispatch, deliverysweeper:risk-message-deliverySweeper risk: may drop, duplicate, misroute, or suppress messagestype/bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions