Spawn a testing agent subprocess for batch regression testing. Selects a prioritized batch of passing features using weighted scoring (via _get_test_batch) and passes them as --testing-feature-ids to the subprocess. Falls back to single --testing-feature-id for batches of on
(self)
| 951 | return True, f"Started batch [{ids_str}]" |
| 952 | |
| 953 | def _spawn_testing_agent(self) -> tuple[bool, str]: |
| 954 | """Spawn a testing agent subprocess for batch regression testing. |
| 955 | |
| 956 | Selects a prioritized batch of passing features using weighted scoring |
| 957 | (via _get_test_batch) and passes them as --testing-feature-ids to the |
| 958 | subprocess. Falls back to single --testing-feature-id for batches of one. |
| 959 | |
| 960 | Multiple testing agents can test the same feature concurrently - this is |
| 961 | intentional and simplifies the architecture by removing claim coordination. |
| 962 | """ |
| 963 | # Check limits first (under lock) |
| 964 | with self._lock: |
| 965 | current_testing_count = len(self.running_testing_agents) |
| 966 | if current_testing_count >= self.max_concurrency: |
| 967 | debug_log.log("TESTING", f"Skipped spawn - at max testing agents ({current_testing_count}/{self.max_concurrency})") |
| 968 | return False, f"At max testing agents ({current_testing_count})" |
| 969 | total_agents = len(self.running_coding_agents) + len(self.running_testing_agents) |
| 970 | if total_agents >= MAX_TOTAL_AGENTS: |
| 971 | debug_log.log("TESTING", f"Skipped spawn - at max total agents ({total_agents}/{MAX_TOTAL_AGENTS})") |
| 972 | return False, f"At max total agents ({total_agents})" |
| 973 | |
| 974 | # Select a weighted batch of passing features for regression testing |
| 975 | batch = self._get_test_batch(self.testing_batch_size) |
| 976 | if not batch: |
| 977 | debug_log.log("TESTING", "No features available for testing") |
| 978 | return False, "No features available for testing" |
| 979 | |
| 980 | # Use the first feature ID as the representative for logging/tracking |
| 981 | primary_feature_id = batch[0] |
| 982 | batch_str = ",".join(str(fid) for fid in batch) |
| 983 | debug_log.log("TESTING", f"Selected batch for testing: [{batch_str}]") |
| 984 | |
| 985 | # Spawn the testing agent |
| 986 | with self._lock: |
| 987 | # Re-check limits in case another thread spawned while we were selecting |
| 988 | current_testing_count = len(self.running_testing_agents) |
| 989 | if current_testing_count >= self.max_concurrency: |
| 990 | return False, f"At max testing agents ({current_testing_count})" |
| 991 | |
| 992 | cmd = [ |
| 993 | sys.executable, |
| 994 | "-u", |
| 995 | str(AUTOFORGE_ROOT / "autonomous_agent_demo.py"), |
| 996 | "--project-dir", str(self.project_dir), |
| 997 | "--max-iterations", "1", |
| 998 | "--agent-type", "testing", |
| 999 | "--testing-feature-ids", batch_str, |
| 1000 | ] |
| 1001 | if self.model: |
| 1002 | cmd.extend(["--model", self.model]) |
| 1003 | |
| 1004 | try: |
| 1005 | # CREATE_NO_WINDOW on Windows prevents console window pop-ups |
| 1006 | # stdin=DEVNULL prevents blocking on stdin reads |
| 1007 | # encoding="utf-8" and errors="replace" fix Windows CP1252 issues |
| 1008 | popen_kwargs: dict[str, Any] = { |
| 1009 | "stdin": subprocess.DEVNULL, |
| 1010 | "stdout": subprocess.PIPE, |
no test coverage detected