Appearance
5.4 Deterministic Middleware Hooks: Loop Detection & Pre-Completion Checklists β
Canonical Curriculum Reference:
docs/plan/05_phase5_harness_engineering_mcp.md
Textbook Reference: AI Agents and Applications (Manning Publications), Chapter 11, Section 11.5 ("The Read-Eval-Print Loop", pp. 307β308) & Chapter 14, Section 14.2 ("Guardrails", pp. 365β373).
π 1. Breaking the "Doom Loop": LoopDetectionMiddleware β
In multi-turn autonomous execution, agents frequently encounter unexpected tool errors (e.g. a file not found, permission denied, or a failing regex). When an LLM lacks external guardrails, it often enters a pathological state known as the Doom Loop:
- The model invokes
edit_file(target="app.py", old="foo", new="bar"). - The tool returns:
Error: 'foo' not found in app.py. - The model re-reads the prompt, hallucinates that the error was a transient glitch, and repeats the exact same call:
edit_file(target="app.py", old="foo", new="bar"). - This cycle repeats indefinitely until context window exhaustion or API timeout.
+-------------------------------------------------------------+
| AGENT COGNITIVE LOOP |
| |
| +--------------------+ +-----------------------+ |
| | Agent Reasoning | ------> | Invoke Tool | |
| +--------------------+ +-----------------------+ |
| ^ | |
| | v |
| +--------------------+ +-----------------------+ |
| | Re-try Same Call | <------ | Tool Returns Failure | |
| +--------------------+ +-----------------------+ |
+-------------------------------------------------------------+
|
INTERCEPTED BY MIDDLEWARE
v
+-------------------------------------------------------------+
| LoopDetectionMiddleware |
| 1. Hash: SHA-256(tool_name + canonical_json(args)) |
| 2. If repetition_count >= 2: |
| - DROP tool call |
| - INJECT system override: "Doom loop detected. STOP. |
| Re-read the file and replan from scratch." |
+-------------------------------------------------------------+Action Hashing & Ring Buffer Mechanics β
The middleware maintains an in-memory ring buffer of the last
If the identical hash appears
β
2. Eliminating Premature Termination: PreCompletionChecklistMiddleware β
A complementary pathology to the Doom Loop is Premature Completion: an agent completes 2 out of 5 required sub-tasks, encounters minor cognitive friction, and outputs a polite summary declaring the mission accomplished.
The PreCompletionChecklistMiddleware enforces a deterministic contract before allowing the agent to exit:
- Completion Interception: Any call to
finish_taskor submission of a final response is intercepted. - Audit Verification: The middleware queries the environment:
- Have unit tests been executed and passed?
- Are there uncommitted changes in git?
- Did the user request specific deliverables that have not been generated?
- Rejection & Redirection: If any checklist item fails, the completion request is rejected, and the agent is forced back into the execution loop with specific missing checklist items.
π» 3. Production Implementation: Deterministic Hooks β
python
import hashlib
import json
from collections import deque
from typing import Any, Final
from pydantic import BaseModel, ConfigDict, Field
class ToolInvocation(BaseModel):
"""Immutable record of an attempted tool execution."""
model_config = ConfigDict(frozen=True)
tool_name: str
arguments: dict[str, Any]
def compute_signature(self) -> str:
serialized = json.dumps(
{"tool": self.tool_name, "args": self.arguments},
sort_keys=True
)
return hashlib.sha256(serialized.encode("utf-8")).hexdigest()
class LoopDetectionMiddleware:
"""Intercepts and breaks repetitive failing actions and doom loops."""
def __init__(self, max_consecutive_repetitions: int = 2, history_window: int = 10) -> None:
self._max_reps: Final[int] = max_consecutive_repetitions
self._history: deque[str] = deque(maxlen=history_window)
def evaluate_invocation(self, invocation: ToolInvocation) -> tuple[bool, str | None]:
"""
Evaluates candidate tool call.
Returns: (allow_execution: bool, feedback_message: str | None)
"""
sig = invocation.compute_signature()
# Check consecutive repetition count
recent_matches = sum(1 for past_sig in list(self._history)[-self._max_reps:] if past_sig == sig)
if recent_matches >= self._max_reps:
feedback = (
f"[HARNESS INTERVENTION]: Doom loop detected. You have invoked tool "
f"'{invocation.tool_name}' with identical arguments {recent_matches + 1} times "
f"without progress. Execution is BLOCKED. You must stop, review recent "
f"observations, and synthesize an entirely new strategy."
)
return False, feedback
self._history.append(sig)
return True, None
class ChecklistItem(BaseModel):
model_config = ConfigDict(frozen=True)
description: str
verified: bool
class PreCompletionChecklistMiddleware:
"""Ensures mandatory quality criteria are satisfied prior to task completion."""
def __init__(self) -> None:
self._checklist: dict[str, bool] = {
"automated_tests_passed": False,
"no_temporary_scratch_files": False,
"documentation_updated": False,
}
def mark_satisfied(self, key: str) -> None:
if key in self._checklist:
self._checklist[key] = True
def evaluate_completion(self) -> tuple[bool, str | None]:
"""Validates if all checklist invariants are satisfied."""
unmet = [k for k, v in self._checklist.items() if not v]
if unmet:
feedback = (
f"[HARNESS INTERVENTION]: Completion rejected. The following mandatory "
f"pre-completion verification items are incomplete: {unmet}. "
f"You cannot finish the task until all gates are verified."
)
return False, feedback
return True, Noneπ§ͺ 4. Automated Verification with Pytest β
python
import pytest
def test_loop_detection_middleware_blocks_repeats():
middleware = LoopDetectionMiddleware(max_consecutive_repetitions=2)
action = ToolInvocation(tool_name="edit_file", arguments={"path": "main.py", "line": 10})
# First call: Allowed
allowed, msg = middleware.evaluate_invocation(action)
assert allowed is True
assert msg is None
# Second call: Allowed
allowed, msg = middleware.evaluate_invocation(action)
assert allowed is True
# Third consecutive call: Blocked by Doom Loop filter
allowed, msg = middleware.evaluate_invocation(action)
assert allowed is False
assert "[HARNESS INTERVENTION]: Doom loop detected." in msg
def test_pre_completion_checklist_enforcement():
checklist = PreCompletionChecklistMiddleware()
# Attempting premature completion
allowed, msg = checklist.evaluate_completion()
assert allowed is False
assert "automated_tests_passed" in msg
# Satisfy all gates
checklist.mark_satisfied("automated_tests_passed")
checklist.mark_satisfied("no_temporary_scratch_files")
checklist.mark_satisfied("documentation_updated")
# Re-evaluate
allowed, msg = checklist.evaluate_completion()
assert allowed is True
assert msg is Noneπ‘οΈ Production Impact β
Implementing these two deterministic hooks in production agent frameworks eliminates:
of runaway token burn incidents caused by models looping on permission/file errors. of "premature victory" completions, ensuring that every pull request or report generated has actually passed automated test gates.