MCPcopy Create free account
hub / github.com/AutoForgeAI/autoforge / get_ready_features

Method get_ready_features

parallel_orchestrator.py:509–579  ·  view source on GitHub ↗

Get features with satisfied dependencies, not already running. Args: feature_dicts: Pre-fetched list of feature dicts. If None, queries the database. scheduling_scores: Pre-computed scheduling scores. If None, computed from feature_dicts.

(
        self,
        feature_dicts: list[dict] | None = None,
        scheduling_scores: dict[int, float] | None = None,
    )

Source from the content-addressed store, hash-verified

507 return resumable
508
509 def get_ready_features(
510 self,
511 feature_dicts: list[dict] | None = None,
512 scheduling_scores: dict[int, float] | None = None,
513 ) -> list[dict]:
514 """Get features with satisfied dependencies, not already running.
515
516 Args:
517 feature_dicts: Pre-fetched list of feature dicts. If None, queries the database.
518 scheduling_scores: Pre-computed scheduling scores. If None, computed from feature_dicts.
519 """
520 if feature_dicts is None:
521 session = self.get_session()
522 try:
523 session.expire_all()
524 all_features = session.query(Feature).all()
525 feature_dicts = [f.to_dict() for f in all_features]
526 finally:
527 session.close()
528
529 # Pre-compute passing_ids once to avoid O(n^2) in the loop
530 passing_ids = {fd["id"] for fd in feature_dicts if fd.get("passes")}
531
532 # Snapshot running IDs once (include all batch feature IDs)
533 with self._lock:
534 running_ids = set(self.running_coding_agents.keys())
535 for batch_ids in self._batch_features.values():
536 running_ids.update(batch_ids)
537
538 ready = []
539 skipped_reasons = {"passes": 0, "in_progress": 0, "running": 0, "failed": 0, "deps": 0}
540 for fd in feature_dicts:
541 if fd.get("passes"):
542 skipped_reasons["passes"] += 1
543 continue
544 if fd.get("in_progress"):
545 skipped_reasons["in_progress"] += 1
546 continue
547 # Skip if already running in this orchestrator
548 if fd["id"] in running_ids:
549 skipped_reasons["running"] += 1
550 continue
551 # Skip if feature has failed too many times
552 if self._failure_counts.get(fd["id"], 0) >= MAX_FEATURE_RETRIES:
553 skipped_reasons["failed"] += 1
554 continue
555 # Check dependencies (pass pre-computed passing_ids)
556 if are_dependencies_satisfied(fd, feature_dicts, passing_ids):
557 ready.append(fd)
558 else:
559 skipped_reasons["deps"] += 1
560
561 # Sort by scheduling score (higher = first), then priority, then id
562 if scheduling_scores is None:
563 scheduling_scores = compute_scheduling_scores(feature_dicts)
564 ready.sort(key=lambda f: (-scheduling_scores.get(f["id"], 0), f["priority"], f["id"]))
565
566 # Summary counts for logging

Callers 1

run_loopMethod · 0.95

Calls 6

get_sessionMethod · 0.95
logMethod · 0.80
to_dictMethod · 0.45
closeMethod · 0.45

Tested by

no test coverage detected