Initialize the orchestrator. Args: project_dir: Path to the project directory max_concurrency: Maximum number of concurrent coding agents (1-5). Also caps testing agents at the same limit. model: Claude model to use (or None for default)
(
self,
project_dir: Path,
max_concurrency: int = DEFAULT_CONCURRENCY,
model: str | None = None,
yolo_mode: bool = False,
testing_agent_ratio: int = 1,
testing_batch_size: int = DEFAULT_TESTING_BATCH_SIZE,
batch_size: int = 3,
on_output: Callable[[int, str], None] | None = None,
on_status: Callable[[int, str], None] | None = None,
)
| 147 | """ |
| 148 | |
| 149 | def __init__( |
| 150 | self, |
| 151 | project_dir: Path, |
| 152 | max_concurrency: int = DEFAULT_CONCURRENCY, |
| 153 | model: str | None = None, |
| 154 | yolo_mode: bool = False, |
| 155 | testing_agent_ratio: int = 1, |
| 156 | testing_batch_size: int = DEFAULT_TESTING_BATCH_SIZE, |
| 157 | batch_size: int = 3, |
| 158 | on_output: Callable[[int, str], None] | None = None, |
| 159 | on_status: Callable[[int, str], None] | None = None, |
| 160 | ): |
| 161 | """Initialize the orchestrator. |
| 162 | |
| 163 | Args: |
| 164 | project_dir: Path to the project directory |
| 165 | max_concurrency: Maximum number of concurrent coding agents (1-5). |
| 166 | Also caps testing agents at the same limit. |
| 167 | model: Claude model to use (or None for default) |
| 168 | yolo_mode: Whether to run in YOLO mode (skip testing agents entirely) |
| 169 | testing_agent_ratio: Number of regression testing agents to maintain (0-3). |
| 170 | 0 = disabled, 1-3 = maintain that many testing agents running independently. |
| 171 | testing_batch_size: Number of features to include per testing session (1-5). |
| 172 | Each testing agent receives this many features to regression test. |
| 173 | on_output: Callback for agent output (feature_id, line) |
| 174 | on_status: Callback for agent status changes (feature_id, status) |
| 175 | """ |
| 176 | self.project_dir = project_dir |
| 177 | self.max_concurrency = min(max(max_concurrency, 1), MAX_PARALLEL_AGENTS) |
| 178 | self.model = model |
| 179 | self.yolo_mode = yolo_mode |
| 180 | self.testing_agent_ratio = min(max(testing_agent_ratio, 0), 3) # Clamp 0-3 |
| 181 | self.testing_batch_size = min(max(testing_batch_size, 1), 5) # Clamp 1-5 |
| 182 | self.batch_size = min(max(batch_size, 1), 3) # Clamp 1-3 |
| 183 | self.on_output = on_output |
| 184 | self.on_status = on_status |
| 185 | |
| 186 | # Thread-safe state |
| 187 | self._lock = threading.Lock() |
| 188 | # Coding agents: feature_id -> process |
| 189 | # Safe to key by feature_id because start_feature() checks for duplicates before spawning |
| 190 | self.running_coding_agents: dict[int, subprocess.Popen] = {} |
| 191 | # Testing agents: pid -> (feature_id, process) |
| 192 | # Keyed by PID (not feature_id) because multiple agents can test the same feature |
| 193 | self.running_testing_agents: dict[int, tuple[int, subprocess.Popen]] = {} |
| 194 | # Legacy alias for backward compatibility |
| 195 | self.running_agents = self.running_coding_agents |
| 196 | self.abort_events: dict[int, threading.Event] = {} |
| 197 | self.is_running = False |
| 198 | |
| 199 | # Track feature failures to prevent infinite retry loops |
| 200 | self._failure_counts: dict[int, int] = {} |
| 201 | |
| 202 | # Track recently tested feature IDs to avoid redundant re-testing. |
| 203 | # Cleared when all passing features have been covered at least once. |
| 204 | self._recently_tested: set[int] = set() |
| 205 | |
| 206 | # Batch tracking: primary feature_id -> all feature IDs in batch |
nothing calls this directly
no test coverage detected