fix: prevent testing agents from running indefinitely

This fix addresses two root causes that caused testing agents to
accumulate (10-12 agents) instead of maintaining a 1:1 ratio with
coding agents:

1. Testing agents now exit after one session (agent.py)
   - Added `or agent_type == "testing"` to the exit condition
   - Previously, testing agents never hit the exit condition since
     they're spawned with feature_id=None

2. Testing agents now spawn when coding agents START, not complete
   - Moved spawn logic from _on_agent_complete() to start_feature()
   - Removed the old spawn logic from _on_agent_complete()
   - This ensures proper 1:1 ratio and prevents accumulation

Expected behavior after fix:
- First coding agent: no testing agent (no passing features yet)
- Subsequent coding agents: one testing agent spawns per start
- Each testing agent tests ONE feature then terminates immediately

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Auto
2026-01-22 08:23:47 +02:00
parent f9d9ad9b85
commit 33e9f38633
2 changed files with 18 additions and 23 deletions

View File

@@ -306,8 +306,11 @@ async def run_autonomous_agent(
print("The autonomous agent has finished its work.")
break
# Single-feature mode: exit after one session (orchestrator manages agents)
if feature_id is not None:
# Single-feature mode OR testing agent: exit after one session
if feature_id is not None or agent_type == "testing":
if agent_type == "testing":
print("\nTesting agent complete. Terminating session.")
else:
print(f"\nSingle-feature mode: Feature #{feature_id} session complete.")
break

View File

@@ -412,9 +412,16 @@ class ParallelOrchestrator:
if not success:
return False, message
# NOTE: Testing agents are spawned in _on_agent_complete() after a coding agent
# succeeds, not here. This ensures we only spawn testing agents when there are
# actually passing features to test.
# Spawn ONE testing agent when coding agent STARTS (if not YOLO mode and passing features exist)
# Testing agents exit after one test, so we spawn fresh ones with each coding agent start
if not self.yolo_mode and self.testing_agent_ratio > 0:
passing_count = self.get_passing_count()
if passing_count > 0:
print(f"[DEBUG] Coding agent started, spawning testing agent (passing_count={passing_count})", flush=True)
debug_log.log("TESTING", "Spawning testing agent on coding agent start",
feature_id=feature_id,
passing_count=passing_count)
self._spawn_testing_agent()
return True, f"Started feature {feature_id}"
@@ -724,23 +731,8 @@ class ParallelOrchestrator:
# CRITICAL: This print triggers the WebSocket to emit agent_update with state='error' or 'success'
print(f"Feature #{feature_id} {status}", flush=True)
# Spawn testing agents after successful coding agent completion
# This is the correct place to spawn testing agents - after we know there are
# passing features (the one this agent just completed, plus any previous ones)
if return_code == 0 and not self.yolo_mode and self.testing_agent_ratio > 0:
passing_count = self.get_passing_count()
print(f"[DEBUG] Coding agent completed successfully, passing_count={passing_count}", flush=True)
debug_log.log("TESTING", "Checking if testing agents should spawn",
yolo_mode=self.yolo_mode,
testing_agent_ratio=self.testing_agent_ratio,
passing_count=passing_count)
if passing_count > 0:
print(f"[DEBUG] Spawning testing agents (ratio={self.testing_agent_ratio})", flush=True)
debug_log.log("TESTING", f"Spawning {self.testing_agent_ratio} testing agent(s)")
self._spawn_testing_agents()
elif return_code == 0:
debug_log.log("TESTING", "Skipping testing agents",
reason="yolo_mode" if self.yolo_mode else f"ratio={self.testing_agent_ratio}")
# NOTE: Testing agents are now spawned in start_feature() when coding agents START,
# not here when they complete. This ensures 1:1 ratio and proper termination.
def stop_feature(self, feature_id: int) -> tuple[bool, str]:
"""Stop a running coding agent and all its child processes."""