diff --git a/agent.py b/agent.py index 09c1a04..10253b5 100644 --- a/agent.py +++ b/agent.py @@ -306,9 +306,12 @@ 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: - print(f"\nSingle-feature mode: Feature #{feature_id} session complete.") + # 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 await asyncio.sleep(delay_seconds) diff --git a/parallel_orchestrator.py b/parallel_orchestrator.py index 09f4e22..5bbcba9 100644 --- a/parallel_orchestrator.py +++ b/parallel_orchestrator.py @@ -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."""