Select a prioritized batch of passing features for regression testing. Uses weighted scoring to prioritize features that: 1. Haven't been tested recently in this orchestrator session 2. Are depended on by many other features (higher impact if broken) 3. Have more dep
(self, batch_size: int = 3)
| 257 | session.close() |
| 258 | |
| 259 | def _get_test_batch(self, batch_size: int = 3) -> list[int]: |
| 260 | """Select a prioritized batch of passing features for regression testing. |
| 261 | |
| 262 | Uses weighted scoring to prioritize features that: |
| 263 | 1. Haven't been tested recently in this orchestrator session |
| 264 | 2. Are depended on by many other features (higher impact if broken) |
| 265 | 3. Have more dependencies themselves (complex integration points) |
| 266 | |
| 267 | When all passing features have been recently tested, the tracking set |
| 268 | is cleared so the cycle starts fresh. |
| 269 | |
| 270 | Args: |
| 271 | batch_size: Maximum number of feature IDs to return (1-5). |
| 272 | |
| 273 | Returns: |
| 274 | List of feature IDs to test, may be shorter than batch_size if |
| 275 | fewer passing features are available. Empty list if none available. |
| 276 | """ |
| 277 | session = self.get_session() |
| 278 | try: |
| 279 | session.expire_all() |
| 280 | passing = ( |
| 281 | session.query(Feature) |
| 282 | .filter(Feature.passes == True) |
| 283 | .filter(Feature.in_progress == False) # Don't test while coding |
| 284 | .all() |
| 285 | ) |
| 286 | |
| 287 | # Extract data from ORM objects before closing the session to avoid |
| 288 | # DetachedInstanceError when accessing attributes after session.close(). |
| 289 | passing_data: list[dict] = [] |
| 290 | for f in passing: |
| 291 | passing_data.append({ |
| 292 | 'id': f.id, |
| 293 | 'dependencies': f.get_dependencies_safe() if hasattr(f, 'get_dependencies_safe') else [], |
| 294 | }) |
| 295 | finally: |
| 296 | session.close() |
| 297 | |
| 298 | if not passing_data: |
| 299 | return [] |
| 300 | |
| 301 | # Build a reverse dependency map: feature_id -> count of features that depend on it. |
| 302 | # The Feature model stores dependencies (what I depend ON), so we invert to find |
| 303 | # dependents (what depends ON me). |
| 304 | dependent_counts: dict[int, int] = {} |
| 305 | for fd in passing_data: |
| 306 | for dep_id in fd['dependencies']: |
| 307 | dependent_counts[dep_id] = dependent_counts.get(dep_id, 0) + 1 |
| 308 | |
| 309 | # Exclude features that are already being tested by running testing agents |
| 310 | # to avoid redundant concurrent testing of the same features. |
| 311 | # running_testing_agents is dict[pid, (primary_feature_id, process)] |
| 312 | with self._lock: |
| 313 | currently_testing_ids: set[int] = set() |
| 314 | for _pid, (feat_id, _proc) in self.running_testing_agents.items(): |
| 315 | currently_testing_ids.add(feat_id) |
| 316 |
no test coverage detected