Completion Gate + Loop Control
Opening Explanation
This stage exists to stop the agent from ending too early. By the time execution reaches here, the agent has already run a command, seen the result, and may believe the task is done. The problem is that agents often claim success one turn too soon. So this stage acts as a completion gate: it does not trust a single “I’m finished” signal. It asks for a second confirmation on the next turn before it really exits. If the task is not complete, it simply feeds the latest observation back into the loop so the agent can think again. In other words, this stage owns the decision between three paths: continue working, ask for completion confirmation, or finally stop.
Main Flow
- The stage looks at the result of the just-finished turn.
It checks whether the agent currently thinks the task is complete, and whether that same completion claim was already pending from the previous turn.
- If this is the first completion claim, it does not end the run yet.
Instead, it uses
_get_completion_confirmation_message()(builds the follow-up prompt that asks the agent to confirm it is truly done) and sends that back into the loop as the next prompt. Why: one completion claim is cheap and often wrong. The extra turn is a safety check. - If the agent says the task is complete again on the next turn, the loop returns.
This is the real exit. At that point, the system has two consecutive signals that the work is done.
- If the task is not complete, the stage takes the normal path.
It sets the next prompt to the latest observation, usually terminal output (text from the shell after the command ran), so the next loop iteration can reason over fresh evidence and decide what to do next.
Terminus2._run_agent_loop()(the main think-act-observe loop for the agent) owns this control point because this is where the system must choose whether to keep iterating or stop.This logic belongs at the end of the iteration body, after the agent has both acted and seen the result.
State Flow
- writes: 无 — this stage is not mapped to any explicit skeleton register in the provided input
- reads: 无 — completion and pending-confirmation signals are used here in code flow, but no explicit register IDs were provided for them
- clears: 无
- triggers downstream:
subsys-parser-internal Parser internal helpers— on the normal continue path, when the loop sets the next prompt and starts another iteration - triggers downstream:
subsys-parser-internal Parser internal helpers— on the first completion claim, when the confirmation prompt is injected and the loop continues - triggers downstream: 无 — on second consecutive completion confirmation, the loop returns and no next stage runs
Pipeline Hand-Off
Upstream, this stage receives the finished turn: the latest observation from command execution, plus the agent’s current judgment about whether the task is done. It produces either a final stop, a confirmation prompt, or the normal next prompt, and that output determines whether the next loop iteration begins and feeds back into parsing and planning.
Terminus2._run_agent_loopterminus_2.py:1556–1567 ↗
Loop-exit gate and prompt carry-forward
Stage context: This region runs at the end of one
_run_agent_loopiteration. It uses the current iteration results—is_task_complete,was_pending_completion, andobservation—to decide whether the loop returns, continues immediately with a confirmation prompt, or falls through with the terminal output as the nextprompt. Within stage 4.10, this is the stage's control gate.
What this code does
This region first calls _dump_trajectory() to persist the current trajectory state. It then inspects is_task_complete together with was_pending_completion to choose one of three outcomes: early return on a second consecutive completion signal, prompt = observation plus continue on a first completion signal, or prompt = observation on the normal path. Its direct outputs are loop control (return or continue) and reassignment of the local prompt variable.
Interface · params / IO
(self, prompt: str) -> None
- params:
self:Terminus2— owning runner instance; supplies_dump_trajectory()and trajectory state;prompt:str— current loop prompt; this region may replace it withobservationfor the next iteration;is_task_complete:?— local completion flag read by this region;was_pending_completion:?— local snapshot of prior pending-completion state used to confirm completion;observation:?— local observation text forwarded intoprompton both non-return paths - reads:
self._trajectory_steps (indirectly via_dump_trajectory()),other instance state consumed byself._dump_trajectory()for serialization/output - returns: Returns
Noneonly on the confirmed-complete branch; otherwise the real product is loop control (continueor fallthrough) and updated localprompt. - effects: calls
self._dump_trajectory()to write trajectory data to external storage; reassigns localprompttoobservationon both non-return paths; issuescontinueon the first-completion branch; issues earlyreturnon the confirmed-complete branch
Execution flow
- Call
self._dump_trajectory()before making the loop-control decision. - If
is_task_completeis true andwas_pending_completionis also true, treat this as the second consecutive completion signal andreturnimmediately. - If
is_task_completeis true butwas_pending_completionis false, setprompt = observationandcontinueso the next iteration uses that confirmation text. - If
is_task_completeis false, skip the completion gate and setprompt = observationon the normal path. - The explicit
continuein the first-completion branch bypasses the trailingprompt = observation, which is whyprompt = observationappears both inside that branch and after theif.
Source
self._dump_trajectory()
if is_task_complete:
if was_pending_completion:
# Task is confirmed complete (this is the second time task_complete was True), return
return
else:
# First completion attempt - ask for confirmation and continue
prompt = observation
continue
prompt = observation
Non-obvious design decisions
- The code uses a two-step completion gate anchored to
is_task_completeandwas_pending_completion. The inline comments define the firstTrueas a 'First completion attempt' and the second consecutiveTrueas 'Task is confirmed complete'. - The region persists state with
_dump_trajectory()before anyreturnorcontinue. That ordering ensures the current iteration's trajectory is written even when the loop exits immediately or restarts from the first-completion branch. - The first-completion path sets
prompt = observationinside the branch because the followingcontinueskips the shared trailing assignment. The duplicated assignment keeps both non-return paths feedingobservationinto the next prompt.
Relations
- Callers:
Terminus2._run_agent_loopiteration tail - Core callees:
Terminus2._dump_trajectory - Config / state sources:
is_task_completelocal result from earlier in_run_agent_loop;was_pending_completionlocal snapshot from earlier in_run_agent_loop;observationlocal output from earlier in_run_agent_loop; instance trajectory state read indirectly by_dump_trajectory() - Results to: local
promptfor the next_run_agent_loopiteration; loop control in_run_agent_loopviacontinue; function exit from_run_agent_loopvia earlyreturn; persisted trajectory output written by_dump_trajectory()
reg-pending-completion— useswas_pending_completionto confirm second completion
Terminus2._get_completion_confirmation_messageterminus_2.py:489–510 ↗
Build completion reconfirmation prompt for active parser
Stage context: This helper supports the stage-4.10 completion gate by generating the exact follow-up prompt used on the first completion claim.
_run_agent_loopuses its return value when it needs the agent to confirm completion for a second consecutive turn. It complements the sibling loop-control logic by supplying the parser-specific wording that stage-4.10 feeds back as the nextprompt.
What this code does
The function builds a confirmation message from terminal_output and the current parser mode in self._parser_name. It returns one string that embeds the terminal state and tells the agent which completion token to repeat: JSON uses "task_complete": true, while XML uses <task_complete>true</task_complete>. It does not mutate instance state or perform I/O. If self._parser_name is neither "json" nor "xml", it raises ValueError.
Interface · params / IO
(self, terminal_output: str) -> str
- params:
terminal_output:str— Current terminal state to embed in the confirmation prompt - reads:
self._parser_name - returns: A parser-specific completion confirmation message string that includes
terminal_outputand instructs the agent to repeat the completion marker
Execution flow
- Read
self._parser_nameto choose the response format that the follow-up prompt must target. - If
self._parser_name == "json", return a message that includesterminal_outputand tells the agent to include"task_complete": trueagain in its JSON response. - If
self._parser_name == "xml", return a message that includesterminal_outputand tells the agent to include<task_complete>true</task_complete>again in its XML response. - For any other
self._parser_name, raiseValueErrorwith the unsupported name in the message instead of returning a fallback prompt.
Source
def _get_completion_confirmation_message(self, terminal_output: str) -> str:
"""Return the format-specific task completion confirmation message."""
if self._parser_name == "json":
return (
f"Current terminal state:\n{terminal_output}\n\n"
"Are you sure you want to mark the task as complete? "
"This will trigger your solution to be graded and you won't be able to "
'make any further corrections. If so, include "task_complete": true '
"in your JSON response again."
)
elif self._parser_name == "xml":
return (
f"Current terminal state:\n{terminal_output}\n\n"
"Are you sure you want to mark the task as complete? "
"This will trigger your solution to be graded and you won't be able to "
"make any further corrections. If so, include "
"<task_complete>true</task_complete> again."
)
else:
raise ValueError(
f"Unknown parser_name: {self._parser_name}. Use 'json' or 'xml'."
)
Non-obvious design decisions
- It branches explicitly on
self._parser_nameso the confirmation text matches the active output schema exactly. A generic confirmation prompt would risk the agent reconfirming in the wrong format and breaking the parser contract. - It embeds
terminal_outputdirectly into the returned string. That keeps the confirmation tied to the current terminal state the agent is about to finalize against, rather than asking for a blind second confirmation. - It raises
ValueErrorin theelsebranch instead of defaulting to one format. That fail-fast choice exposes parser misconfiguration immediately; a silent default would produce a misleading prompt and likely cause a harder-to-debug downstream parse failure.
Relations
- Callers:
Terminus2._run_agent_loopwhen the first completion signal needs explicit reconfirmation - Core callees: none
- Config / state sources:
self._parser_nameselects JSON versus XML wording - Results to: Returned string becomes the confirmation question fed back into
_run_agent_loopas the nextprompt; Its text supports the two-step completion gate described by stage-4.10 - Related siblings:
Terminus2._run_agent_loopconsumes this helper's string during the stage-4.10 continue path