← back to hsubra89__rlph

Function bodies 309 total

All specs Real LLM only Function bodies
test_review_phase_overrides_runner_gets_correct_defaults function · rust · L1014-L1042 (29 LOC)
src/config.rs
    fn test_review_phase_overrides_runner_gets_correct_defaults() {
        let tmp = tempfile::tempdir().unwrap();
        let cfg_dir = tmp.path().join(".rlph");
        std::fs::create_dir_all(&cfg_dir).unwrap();
        std::fs::write(
            cfg_dir.join("config.toml"),
            r#"
runner = "codex"

[[review_phases]]
name = "check"
prompt = "check-review"
runner = "claude"
"#,
        )
        .unwrap();
        let cli = Cli::parse_from(["rlph", "--once"]);
        let config = Config::load_from(&cli, tmp.path()).unwrap();
        assert_eq!(config.review_phases[0].runner, "claude");
        assert_eq!(config.review_phases[0].agent_binary, "claude");
        assert_eq!(
            config.review_phases[0].agent_model.as_deref(),
            Some("claude-opus-4-6")
        );
        assert_eq!(
            config.review_phases[0].agent_effort.as_deref(),
            Some("high")
        );
    }
test_review_step_overrides_runner_gets_correct_defaults function · rust · L1045-L1071 (27 LOC)
src/config.rs
    fn test_review_step_overrides_runner_gets_correct_defaults() {
        let tmp = tempfile::tempdir().unwrap();
        let cfg_dir = tmp.path().join(".rlph");
        std::fs::create_dir_all(&cfg_dir).unwrap();
        std::fs::write(
            cfg_dir.join("config.toml"),
            r#"
runner = "codex"

[[review_phases]]
name = "check"
prompt = "check-review"

[review_aggregate]
runner = "claude"
"#,
        )
        .unwrap();
        let cli = Cli::parse_from(["rlph", "--once"]);
        let config = Config::load_from(&cli, tmp.path()).unwrap();
        assert_eq!(config.review_aggregate.runner, "claude");
        assert_eq!(config.review_aggregate.agent_binary, "claude");
        assert_eq!(
            config.review_aggregate.agent_model.as_deref(),
            Some("claude-opus-4-6")
        );
    }
test_review_phase_inherits_global_agent_overrides function · rust · L1074-L1103 (30 LOC)
src/config.rs
    fn test_review_phase_inherits_global_agent_overrides() {
        let tmp = tempfile::tempdir().unwrap();
        let cfg_dir = tmp.path().join(".rlph");
        std::fs::create_dir_all(&cfg_dir).unwrap();
        std::fs::write(
            cfg_dir.join("config.toml"),
            r#"
runner = "codex"
agent_binary = "/opt/agent-proxy"
agent_model = "custom-model-v1"
agent_effort = "medium"

[[review_phases]]
name = "check"
prompt = "check-review"
"#,
        )
        .unwrap();
        let cli = Cli::parse_from(["rlph", "--once"]);
        let config = Config::load_from(&cli, tmp.path()).unwrap();
        assert_eq!(config.review_phases[0].agent_binary, "/opt/agent-proxy");
        assert_eq!(
            config.review_phases[0].agent_model.as_deref(),
            Some("custom-model-v1")
        );
        assert_eq!(
            config.review_phases[0].agent_effort.as_deref(),
            Some("medium")
        );
    }
test_review_steps_inherit_global_agent_overrides function · rust · L1106-L1149 (44 LOC)
src/config.rs
    fn test_review_steps_inherit_global_agent_overrides() {
        let tmp = tempfile::tempdir().unwrap();
        let cfg_dir = tmp.path().join(".rlph");
        std::fs::create_dir_all(&cfg_dir).unwrap();
        std::fs::write(
            cfg_dir.join("config.toml"),
            r#"
runner = "codex"
agent_binary = "/opt/agent-proxy"
agent_model = "custom-model-v1"
agent_effort = "medium"

[[review_phases]]
name = "check"
prompt = "check-review"

[review_aggregate]
prompt = "review-aggregate"

[review_fix]
prompt = "review-fix"
"#,
        )
        .unwrap();
        let cli = Cli::parse_from(["rlph", "--once"]);
        let config = Config::load_from(&cli, tmp.path()).unwrap();

        assert_eq!(config.review_aggregate.agent_binary, "/opt/agent-proxy");
        assert_eq!(
            config.review_aggregate.agent_model.as_deref(),
            Some("custom-model-v1")
        );
        assert_eq!(
            config.review_aggregate.agent_effort.as_deref(),
            Some("me
parse_dependencies function · rust · L14-L38 (25 LOC)
src/deps.rs
pub fn parse_dependencies(body: &str) -> Vec<u64> {
    let mut deps = Vec::new();

    // "blocked by #N" or "depends on #N"
    let inline_re = Regex::new(r"(?i)(?:blocked\s+by|depends\s+on)\s+#(\d+)").unwrap();
    for cap in inline_re.captures_iter(body) {
        if let Ok(n) = cap[1].parse::<u64>() {
            deps.push(n);
        }
    }

    // "blockedBy: [N, M, ...]"
    let list_re = Regex::new(r"(?i)blockedBy:\s*\[([^\]]+)\]").unwrap();
    for cap in list_re.captures_iter(body) {
        for num_str in cap[1].split(',') {
            if let Ok(n) = num_str.trim().parse::<u64>() {
                deps.push(n);
            }
        }
    }

    deps.sort_unstable();
    deps.dedup();
    deps
}
build function · rust · L58-L71 (14 LOC)
src/deps.rs
    pub fn build(tasks: &[Task]) -> Self {
        let mut edges = HashMap::new();
        for task in tasks {
            let id: u64 = match task.id.parse() {
                Ok(n) => n,
                Err(_) => continue,
            };
            let deps = parse_dependencies(&task.body);
            if !deps.is_empty() {
                edges.insert(id, deps.into_iter().collect());
            }
        }
        Self { edges }
    }
cycle_peers function · rust · L72-L121 (50 LOC)
src/deps.rs
    fn cycle_peers(&self) -> (HashMap<u64, HashSet<u64>>, Vec<Vec<u64>>) {
        let all_nodes: HashSet<u64> = self
            .edges
            .keys()
            .chain(self.edges.values().flat_map(|deps| deps.iter()))
            .copied()
            .collect();

        let mut nodes: Vec<u64> = all_nodes.into_iter().collect();
        nodes.sort_unstable();

        let mut state = TarjanState::default();

        for node in nodes {
            if !state.indices.contains_key(&node) {
                self.tarjan_strong_connect(node, &mut state);
            }
        }

        let mut cycle_peers: HashMap<u64, HashSet<u64>> = HashMap::new();
        let mut cycles_for_log = Vec::new();

        for component in state.components {
            let has_self_loop = component
                .iter()
                .any(|node| self.edges.get(node).is_some_and(|deps| deps.contains(node)));
            if component.len() <= 1 && !has_self_loop {
                continue;
        
Open data scored by Repobility · https://repobility.com
tarjan_strong_connect function · rust · L122-L161 (40 LOC)
src/deps.rs
    fn tarjan_strong_connect(&self, node: u64, state: &mut TarjanState) {
        state.indices.insert(node, state.index);
        state.lowlink.insert(node, state.index);
        state.index += 1;
        state.stack.push(node);
        state.on_stack.insert(node);

        if let Some(deps) = self.edges.get(&node) {
            let mut sorted_deps: Vec<u64> = deps.iter().copied().collect();
            sorted_deps.sort_unstable();

            for dep in sorted_deps {
                if !state.indices.contains_key(&dep) {
                    self.tarjan_strong_connect(dep, state);
                    let dep_low = state.lowlink[&dep];
                    if let Some(node_low) = state.lowlink.get_mut(&node) {
                        *node_low = (*node_low).min(dep_low);
                    }
                } else if state.on_stack.contains(&dep) {
                    let dep_index = state.indices[&dep];
                    if let Some(node_low) = state.lowlink.get_mut(&node) {
     
filter_eligible function · rust · L166-L198 (33 LOC)
src/deps.rs
    pub fn filter_eligible(&self, tasks: Vec<Task>, done_ids: &HashSet<u64>) -> Vec<Task> {
        let (cycle_peers, cycles_for_log) = self.cycle_peers();

        if !cycles_for_log.is_empty() {
            warn!(
                cycles = ?cycles_for_log,
                "dependency cycles detected; ignoring cycle-internal blockers (external blockers still enforced)"
            );
        }

        tasks
            .into_iter()
            .filter(|task| {
                let id: u64 = match task.id.parse() {
                    Ok(n) => n,
                    Err(_) => return true,
                };
                match self.edges.get(&id) {
                    None => true,
                    Some(deps) => {
                        if let Some(peers) = cycle_peers.get(&id) {
                            // Ignore same-cycle blockers but still enforce external ones
                            deps.iter()
                                .filter(|dep| !peers.contains(dep))
      
make_task function · rust · L204-L214 (11 LOC)
src/deps.rs
    fn make_task(id: u64, body: &str) -> Task {
        Task {
            id: id.to_string(),
            title: format!("Task {id}"),
            body: body.to_string(),
            labels: vec![],
            url: String::new(),
            priority: None,
        }
    }
test_graph_no_deps function · rust · L263-L269 (7 LOC)
src/deps.rs
    fn test_graph_no_deps() {
        let tasks = vec![make_task(1, "No deps"), make_task(2, "Also none")];
        let graph = DependencyGraph::build(&tasks);
        let done = HashSet::new();
        let eligible = graph.filter_eligible(tasks, &done);
        assert_eq!(eligible.len(), 2);
    }
test_graph_filters_blocked function · rust · L272-L283 (12 LOC)
src/deps.rs
    fn test_graph_filters_blocked() {
        let tasks = vec![
            make_task(1, "No deps"),
            make_task(2, "Blocked by #1"),
            make_task(3, "Blocked by #99"),
        ];
        let graph = DependencyGraph::build(&tasks);
        let done = HashSet::new();
        let eligible = graph.filter_eligible(tasks, &done);
        assert_eq!(eligible.len(), 1);
        assert_eq!(eligible[0].id, "1");
    }
test_graph_unblocks_when_done function · rust · L286-L292 (7 LOC)
src/deps.rs
    fn test_graph_unblocks_when_done() {
        let tasks = vec![make_task(1, "No deps"), make_task(2, "Blocked by #1")];
        let graph = DependencyGraph::build(&tasks);
        let done: HashSet<u64> = [1].into_iter().collect();
        let eligible = graph.filter_eligible(tasks, &done);
        assert_eq!(eligible.len(), 2);
    }
test_graph_partial_unblock function · rust · L295-L303 (9 LOC)
src/deps.rs
    fn test_graph_partial_unblock() {
        let tasks = vec![make_task(1, "No deps"), make_task(2, "blockedBy: [1, 99]")];
        let graph = DependencyGraph::build(&tasks);
        let done: HashSet<u64> = [1].into_iter().collect();
        let eligible = graph.filter_eligible(tasks, &done);
        // Task 2 still blocked by #99
        assert_eq!(eligible.len(), 1);
        assert_eq!(eligible[0].id, "1");
    }
test_cycle_treated_as_unblocked function · rust · L306-L316 (11 LOC)
src/deps.rs
    fn test_cycle_treated_as_unblocked() {
        let tasks = vec![
            make_task(1, "Blocked by #2"),
            make_task(2, "Blocked by #1"),
            make_task(3, "No deps"),
        ];
        let graph = DependencyGraph::build(&tasks);
        let done = HashSet::new();
        let eligible = graph.filter_eligible(tasks, &done);
        assert_eq!(eligible.len(), 3);
    }
All rows above produced by Repobility · https://repobility.com
test_three_node_cycle_all_eligible function · rust · L319-L329 (11 LOC)
src/deps.rs
    fn test_three_node_cycle_all_eligible() {
        let tasks = vec![
            make_task(1, "Blocked by #3"),
            make_task(2, "Blocked by #1"),
            make_task(3, "Blocked by #2"),
        ];
        let graph = DependencyGraph::build(&tasks);
        let done = HashSet::new();
        let eligible = graph.filter_eligible(tasks, &done);
        assert_eq!(eligible.len(), 3);
    }
test_mixed_blocked_and_cycle function · rust · L332-L348 (17 LOC)
src/deps.rs
    fn test_mixed_blocked_and_cycle() {
        let tasks = vec![
            make_task(1, "Blocked by #2"),
            make_task(2, "Blocked by #1"),
            make_task(3, "Blocked by #99"), // blocked by external, not a cycle
            make_task(4, "No deps"),
        ];
        let graph = DependencyGraph::build(&tasks);
        let done = HashSet::new();
        let eligible = graph.filter_eligible(tasks, &done);
        // 1,2 in cycle (unblocked), 3 blocked by #99, 4 no deps
        assert_eq!(eligible.len(), 3);
        let ids: Vec<&str> = eligible.iter().map(|t| t.id.as_str()).collect();
        assert!(ids.contains(&"1"));
        assert!(ids.contains(&"2"));
        assert!(ids.contains(&"4"));
    }
test_cycle_task_with_external_blocker_is_blocked function · rust · L353-L366 (14 LOC)
src/deps.rs
    fn test_cycle_task_with_external_blocker_is_blocked() {
        // Task 1 and 2 form a cycle, but task 1 also depends on external #99
        let tasks = vec![
            make_task(1, "Blocked by #2\nBlocked by #99"),
            make_task(2, "Blocked by #1"),
        ];
        let graph = DependencyGraph::build(&tasks);
        let done = HashSet::new();
        let eligible = graph.filter_eligible(tasks, &done);
        // Task 1: in cycle but blocked by external #99 → blocked
        // Task 2: in cycle, only cycle-internal dep → eligible
        assert_eq!(eligible.len(), 1);
        assert_eq!(eligible[0].id, "2");
    }
test_cycle_task_external_blocker_resolved function · rust · L369-L380 (12 LOC)
src/deps.rs
    fn test_cycle_task_external_blocker_resolved() {
        // Same as above but #99 is now done
        let tasks = vec![
            make_task(1, "Blocked by #2\nBlocked by #99"),
            make_task(2, "Blocked by #1"),
        ];
        let graph = DependencyGraph::build(&tasks);
        let done: HashSet<u64> = [99].into_iter().collect();
        let eligible = graph.filter_eligible(tasks, &done);
        // Both eligible: cycle deps ignored, external #99 is done
        assert_eq!(eligible.len(), 2);
    }
test_cycle_with_multiple_external_blockers function · rust · L383-L410 (28 LOC)
src/deps.rs
    fn test_cycle_with_multiple_external_blockers() {
        // 3-node cycle where one node has external deps
        let tasks = vec![
            make_task(1, "Blocked by #3\nBlocked by #50"),
            make_task(2, "Blocked by #1"),
            make_task(3, "Blocked by #2\nBlocked by #60"),
        ];
        let graph = DependencyGraph::build(&tasks);

        // Nothing done: 1 blocked by #50, 3 blocked by #60, 2 only has cycle dep
        let done = HashSet::new();
        let eligible = graph.filter_eligible(tasks.clone(), &done);
        assert_eq!(eligible.len(), 1);
        assert_eq!(eligible[0].id, "2");

        // #50 done: 1 unblocked, 3 still blocked by #60
        let done: HashSet<u64> = [50].into_iter().collect();
        let eligible = graph.filter_eligible(tasks.clone(), &done);
        assert_eq!(eligible.len(), 2);
        let ids: Vec<&str> = eligible.iter().map(|t| t.id.as_str()).collect();
        assert!(ids.contains(&"1"));
        assert!(ids.contains(&"
test_pure_cycle_no_external_still_eligible function · rust · L413-L420 (8 LOC)
src/deps.rs
    fn test_pure_cycle_no_external_still_eligible() {
        // Pure cycle with no external deps — existing behavior preserved
        let tasks = vec![make_task(1, "Blocked by #2"), make_task(2, "Blocked by #1")];
        let graph = DependencyGraph::build(&tasks);
        let done = HashSet::new();
        let eligible = graph.filter_eligible(tasks, &done);
        assert_eq!(eligible.len(), 2);
    }
test_cross_cycle_dependency_still_enforced function · rust · L423-L450 (28 LOC)
src/deps.rs
    fn test_cross_cycle_dependency_still_enforced() {
        // Two separate cycles: {1,2} and {3,4}
        // Task 1 also depends on task 3 (cross-cycle dep) — must be enforced
        let tasks = vec![
            make_task(1, "Blocked by #2\nBlocked by #3"),
            make_task(2, "Blocked by #1"),
            make_task(3, "Blocked by #4"),
            make_task(4, "Blocked by #3"),
        ];
        let graph = DependencyGraph::build(&tasks);
        let done = HashSet::new();
        let eligible = graph.filter_eligible(tasks.clone(), &done);
        // Task 1: cycle peer is 2, dep on 3 is cross-cycle → blocked
        // Task 2: only cycle-internal dep on 1 → eligible
        // Task 3: cycle peer is 4, no external deps → eligible
        // Task 4: cycle peer is 3, no external deps → eligible
        assert_eq!(eligible.len(), 3);
        let ids: Vec<&str> = eligible.iter().map(|t| t.id.as_str()).collect();
        assert!(!ids.contains(&"1"));
        assert!(ids.contains
test_non_cycle_tasks_unaffected_by_fix function · rust · L453-L473 (21 LOC)
src/deps.rs
    fn test_non_cycle_tasks_unaffected_by_fix() {
        // Non-cycle tasks keep standard blocked/unblocked behavior
        let tasks = vec![
            make_task(10, "Blocked by #20"),
            make_task(20, "No deps"),
            make_task(30, "Blocked by #10"),
        ];
        let graph = DependencyGraph::build(&tasks);

        let done = HashSet::new();
        let eligible = graph.filter_eligible(tasks.clone(), &done);
        assert_eq!(eligible.len(), 1);
        assert_eq!(eligible[0].id, "20");

        let done: HashSet<u64> = [20].into_iter().collect();
        let eligible = graph.filter_eligible(tasks, &done);
        assert_eq!(eligible.len(), 2);
        let ids: Vec<&str> = eligible.iter().map(|t| t.id.as_str()).collect();
        assert!(ids.contains(&"10"));
        assert!(ids.contains(&"20"));
    }
Repobility · code-quality intelligence platform · https://repobility.com
test_scc_cycle_internal_dep_not_misclassified_as_external function · rust · L476-L493 (18 LOC)
src/deps.rs
    fn test_scc_cycle_internal_dep_not_misclassified_as_external() {
        // Single SCC: 1 -> {2,3}, 2 -> {1}, 3 -> {2}
        // All dependencies are cycle-internal, so all tasks should remain eligible.
        let tasks = vec![
            make_task(1, "Blocked by #2\nBlocked by #3"),
            make_task(2, "Blocked by #1"),
            make_task(3, "Blocked by #2"),
        ];
        let graph = DependencyGraph::build(&tasks);
        let done = HashSet::new();
        let eligible = graph.filter_eligible(tasks, &done);

        assert_eq!(eligible.len(), 3);
        let ids: Vec<&str> = eligible.iter().map(|t| t.id.as_str()).collect();
        assert!(ids.contains(&"1"));
        assert!(ids.contains(&"2"));
        assert!(ids.contains(&"3"));
    }
init_logging function · rust · L23-L32 (10 LOC)
src/main.rs
fn init_logging() {
    tracing_subscriber::fmt()
        .with_env_filter(
            EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("warn")),
        )
        .with_target(true)
        .without_time()
        .init();
}
create_phase_runner function · rust · L61-L70 (10 LOC)
src/orchestrator.rs
    fn create_phase_runner(&self, phase: &ReviewPhaseConfig, timeout_retries: u32) -> AnyRunner {
        build_runner(
            &phase.runner,
            &phase.agent_binary,
            phase.agent_model.as_deref(),
            phase.agent_effort.as_deref(),
            phase.agent_timeout.map(Duration::from_secs),
            timeout_retries,
        )
    }
create_step_runner function · rust · L71-L81 (11 LOC)
src/orchestrator.rs
    fn create_step_runner(&self, step: &ReviewStepConfig, timeout_retries: u32) -> AnyRunner {
        build_runner(
            &step.runner,
            &step.agent_binary,
            step.agent_model.as_deref(),
            step.agent_effort.as_deref(),
            step.agent_timeout.map(Duration::from_secs),
            timeout_retries,
        )
    }
phases_started function · rust · L132-L139 (8 LOC)
src/orchestrator.rs
    fn phases_started(&self, names: &[String]) {
        eprintln!(
            "[rlph] Running {} review agents: {}",
            names.len(),
            names.join(", ")
        );
    }
new function · rust · L169-L191 (23 LOC)
src/orchestrator.rs
    pub fn new(
        source: S,
        runner: R,
        submission: B,
        worktree_mgr: WorktreeManager,
        state_mgr: StateManager,
        prompt_engine: PromptEngine,
        config: Config,
        repo_root: PathBuf,
    ) -> Self {
        Self {
            source,
            runner,
            submission,
            worktree_mgr,
            state_mgr,
            prompt_engine,
            config,
            repo_root,
            review_factory: DefaultReviewRunnerFactory,
            reporter: StderrReporter,
        }
    }
with_review_factory function · rust · L195-L208 (14 LOC)
src/orchestrator.rs
    pub fn with_review_factory<F2>(self, review_factory: F2) -> Orchestrator<S, R, B, F2, P> {
        Orchestrator {
            source: self.source,
            runner: self.runner,
            submission: self.submission,
            worktree_mgr: self.worktree_mgr,
            state_mgr: self.state_mgr,
            prompt_engine: self.prompt_engine,
            config: self.config,
            repo_root: self.repo_root,
            review_factory,
            reporter: self.reporter,
        }
    }
with_reporter function · rust · L209-L223 (15 LOC)
src/orchestrator.rs
    pub fn with_reporter<P2>(self, reporter: P2) -> Orchestrator<S, R, B, F, P2> {
        Orchestrator {
            source: self.source,
            runner: self.runner,
            submission: self.submission,
            worktree_mgr: self.worktree_mgr,
            state_mgr: self.state_mgr,
            prompt_engine: self.prompt_engine,
            config: self.config,
            repo_root: self.repo_root,
            review_factory: self.review_factory,
            reporter,
        }
    }
Repobility's GitHub App fixes findings like these · https://github.com/apps/repobility-bot
run_loop function · rust · L237-L285 (49 LOC)
src/orchestrator.rs
    pub async fn run_loop(&self, mut shutdown: Option<watch::Receiver<bool>>) -> Result<()> {
        if self.config.once {
            return self.run_once().await;
        }

        let mut iterations = 0u32;

        loop {
            if Self::shutdown_requested(shutdown.as_ref()) {
                info!("shutdown requested, exiting loop");
                break;
            }

            self.run_iteration().await?;
            iterations += 1;

            if let Some(max) = self.config.max_iterations
                && iterations >= max
            {
                info!(max, "reached max iterations, exiting");
                break;
            }

            if !self.config.continuous {
                if self.config.max_iterations.is_none() {
                    break;
                }
                continue;
            }

            if Self::shutdown_requested(shutdown.as_ref()) {
                info!("shutdown requested, exiting loop");
                break;
     
run_review_for_existing_pr function · rust · L294-L337 (44 LOC)
src/orchestrator.rs
    pub async fn run_review_for_existing_pr(&self, invocation: ReviewInvocation) -> Result<()> {
        self.state_mgr.set_current_task(
            &invocation.task_id_for_state,
            "review",
            &invocation.worktree_info.path.display().to_string(),
        )?;

        if !self.config.dry_run
            && let Some(task_id) = invocation.mark_in_review_task_id.as_deref()
        {
            self.source.mark_in_review(task_id)?;
        }

        let result = self
            .run_review_pipeline(
                &invocation.vars,
                &invocation.worktree_info,
                invocation.comment_pr_number,
                invocation.push_remote_branch.as_deref(),
                true,
            )
            .await;

        match result {
            Ok(()) => {
                self.state_mgr.complete_current_task()?;

                info!("cleaning up worktree");
                if let Err(e) = self.worktree_mgr.remove(&invocation.worktree_info.pa
run_iteration function · rust · L338-L450 (113 LOC)
src/orchestrator.rs
    async fn run_iteration(&self) -> Result<IterationOutcome> {
        // 1. Fetch eligible tasks and filter by dependency graph
        self.reporter.fetching_tasks();
        info!("fetching eligible tasks");
        let tasks = self.source.fetch_eligible_tasks()?;
        if tasks.is_empty() {
            info!("no eligible tasks found");
            return Ok(IterationOutcome::NoEligibleTasks);
        }

        let done_ids = self.source.fetch_closed_task_ids()?;
        let graph = DependencyGraph::build(&tasks);
        let tasks = graph.filter_eligible(tasks, &done_ids);
        if tasks.is_empty() {
            info!("no unblocked tasks found");
            return Ok(IterationOutcome::NoEligibleTasks);
        }
        info!(count = tasks.len(), "found eligible tasks");
        self.reporter.tasks_found(tasks.len());

        // 2. Choose phase — agent selects a task
        info!("running choose phase");
        let mut choose_vars = HashMap::new();
        choose_vars.in
wait_for_poll_or_shutdown function · rust · L455-L475 (21 LOC)
src/orchestrator.rs
    async fn wait_for_poll_or_shutdown(
        poll_duration: Duration,
        shutdown: &mut Option<watch::Receiver<bool>>,
    ) -> bool {
        if let Some(rx) = shutdown {
            tokio::select! {
                _ = tokio::time::sleep(poll_duration) => false,
                changed = rx.changed() => {
                    if changed.is_ok() {
                        *rx.borrow()
                    } else {
                        false
                    }
                }
            }
        } else {
            tokio::time::sleep(poll_duration).await;
            false
        }
    }
run_implement_review function · rust · L478-L530 (53 LOC)
src/orchestrator.rs
    async fn run_implement_review(
        &self,
        task: &Task,
        issue_number: u64,
        worktree_info: &WorktreeInfo,
        existing_pr_number: Option<u64>,
    ) -> Result<()> {
        let mut vars = self.build_task_vars(task, worktree_info);

        // 7. Implement phase
        self.reporter.implement_started();
        info!("running implement phase");
        let impl_prompt = self.prompt_engine.render_phase("implement", &vars)?;
        self.runner
            .run(Phase::Implement, &impl_prompt, &worktree_info.path)
            .await?;

        // 8. Push branch
        if !self.config.dry_run {
            info!("pushing branch");
            self.push_branch(worktree_info)?;
        }

        // 9. Submit PR (skip if choose agent reported an existing PR)
        let pr_number = if let Some(pr) = existing_pr_number {
            info!(pr, "skipping PR submission — existing PR");
            Some(pr)
        } else if !self.config.dry_run {
            in
parse_task_selection function · rust · L744-L759 (16 LOC)
src/orchestrator.rs
    fn parse_task_selection(&self) -> Result<String> {
        let path = self.repo_root.join(".ralph").join("task.toml");
        let content = std::fs::read_to_string(&path).map_err(|e| {
            Error::Orchestrator(format!(
                "failed to read task selection {}: {e}",
                path.display()
            ))
        })?;
        let selection: TaskSelection = toml::from_str(&content)
            .map_err(|e| Error::Orchestrator(format!("failed to parse task selection: {e}")))?;

        // Clean up the selection file
        let _ = std::fs::remove_file(&path);

        Ok(selection.id)
    }
build_task_vars function · rust · L760-L779 (20 LOC)
src/orchestrator.rs
    fn build_task_vars(&self, task: &Task, worktree: &WorktreeInfo) -> HashMap<String, String> {
        let mut vars = HashMap::new();
        vars.insert("issue_title".to_string(), task.title.clone());
        vars.insert("issue_body".to_string(), task.body.clone());
        vars.insert("issue_number".to_string(), task.id.clone());
        vars.insert("issue_url".to_string(), task.url.clone());
        vars.insert(
            "repo_path".to_string(),
            self.repo_root.display().to_string(),
        );
        vars.insert("branch_name".to_string(), worktree.branch.clone());
        vars.insert(
            "worktree_path".to_string(),
            worktree.path.display().to_string(),
        );
        vars.insert("pr_number".to_string(), String::new());
        vars.insert("pr_branch".to_string(), String::new());
        vars
    }
push_branch function · rust · L780-L795 (16 LOC)
src/orchestrator.rs
    fn push_branch(&self, worktree: &WorktreeInfo) -> Result<()> {
        let output = Command::new("git")
            .args(["push", "-u", "origin", &worktree.branch])
            .current_dir(&worktree.path)
            .output()
            .map_err(|e| Error::Orchestrator(format!("failed to run git push: {e}")))?;

        if !output.status.success() {
            let stderr = String::from_utf8_lossy(&output.stderr);
            return Err(Error::Orchestrator(format!("git push failed: {stderr}")));
        }

        info!(branch = worktree.branch, "pushed branch");
        Ok(())
    }
Open data scored by Repobility · https://repobility.com
push_branch_to function · rust · L796-L815 (20 LOC)
src/orchestrator.rs
    fn push_branch_to(&self, worktree: &WorktreeInfo, remote_branch: &str) -> Result<()> {
        validate_branch_name(remote_branch)
            .map_err(|e| Error::Orchestrator(format!("invalid remote branch name: {e}")))?;

        let refspec = format!("HEAD:{remote_branch}");
        let output = Command::new("git")
            .args(["push", "-u", "origin", &refspec])
            .current_dir(&worktree.path)
            .output()
            .map_err(|e| Error::Orchestrator(format!("failed to run git push: {e}")))?;

        if !output.status.success() {
            let stderr = String::from_utf8_lossy(&output.stderr);
            return Err(Error::Orchestrator(format!("git push failed: {stderr}")));
        }

        info!(branch = worktree.branch, remote_branch, "pushed branch");
        Ok(())
    }
parse_issue_number function · rust · L819-L826 (8 LOC)
src/orchestrator.rs
pub fn parse_issue_number(task_id: &str) -> Result<u64> {
    task_id
        .strip_prefix("gh-")
        .and_then(|n| n.parse::<u64>().ok())
        .ok_or_else(|| {
            Error::Orchestrator(format!("invalid task id: {task_id}, expected gh-<number>"))
        })
}
test_parse_issue_number_invalid function · rust · L840-L846 (7 LOC)
src/orchestrator.rs
    fn test_parse_issue_number_invalid() {
        assert!(parse_issue_number("42").is_err());
        assert!(parse_issue_number("gh-").is_err());
        assert!(parse_issue_number("gh-abc").is_err());
        assert!(parse_issue_number("").is_err());
        assert!(parse_issue_number("linear-42").is_err());
    }
test_parse_aggregator_approved_json function · rust · L849-L858 (10 LOC)
src/orchestrator.rs
    fn test_parse_aggregator_approved_json() {
        use crate::review_schema::{Verdict, parse_aggregator_output};

        let json = r#"{"verdict":"approved","comment":"All looks good.","findings":[],"fix_instructions":null}"#;
        let output = parse_aggregator_output(json).unwrap();
        assert_eq!(output.verdict, Verdict::Approved);
        assert_eq!(output.comment, "All looks good.");
        assert!(output.findings.is_empty());
        assert!(output.fix_instructions.is_none());
    }
test_parse_aggregator_needs_fix_json function · rust · L861-L868 (8 LOC)
src/orchestrator.rs
    fn test_parse_aggregator_needs_fix_json() {
        use crate::review_schema::{Verdict, parse_aggregator_output};

        let json = r#"{"verdict":"needs_fix","comment":"Issues found.","findings":[{"file":"src/main.rs","line":42,"severity":"critical","description":"bug"}],"fix_instructions":"Fix the bug."}"#;
        let output = parse_aggregator_output(json).unwrap();
        assert_eq!(output.verdict, Verdict::NeedsFix);
        assert_eq!(output.fix_instructions.as_deref(), Some("Fix the bug."));
    }
submission_instructions function · rust · L13-L31 (19 LOC)
src/prd.rs
pub fn submission_instructions(source: &str, label: &str) -> String {
    match source {
        "github" => format!(
            "Submit the final PRD as a GitHub issue using the `gh` CLI:\n\
             ```\n\
             gh issue create --label \"{label}\" --title \"PRD: <title>\" --body \"<prd content>\"\n\
             ```\n\
             Use a HEREDOC for the body if it contains special characters.\n\
             Add the label `{label}` to the issue so the autonomous loop can pick it up.",
        ),
        "linear" => format!(
            "Submit the final PRD as a Linear project/issue.\n\
             Use the Linear CLI or API to create the issue with the PRD as its description.\n\
             Ensure it is placed in the correct team and project.\n\
             Tag it with the label `{label}`.",
        ),
        _ => "Submit the final PRD to your configured task source.".to_string(),
    }
}
build_prd_command function · rust · L37-L61 (25 LOC)
src/prd.rs
pub fn build_prd_command(
    config: &Config,
    rendered_prompt: &str,
    description: Option<&str>,
) -> (String, Vec<String>) {
    let mut args = Vec::new();

    // Build the initial user prompt: template + optional description.
    // Passed as a positional argument so the session stays interactive.
    let prompt = match description {
        Some(desc) if !desc.is_empty() => {
            format!("{rendered_prompt}\n\n## Desired Objective\n\n{desc}")
        }
        _ => rendered_prompt.to_string(),
    };

    if let Some(ref model) = config.agent_model {
        args.push("--model".to_string());
        args.push(model.clone());
    }

    args.push(prompt);

    (config.agent_binary.clone(), args)
}
run_prd function · rust · L67-L110 (44 LOC)
src/prd.rs
pub async fn run_prd(config: &Config, description: Option<&str>) -> Result<i32> {
    let override_dir = std::path::Path::new(PROMPT_OVERRIDE_DIR);
    let engine = PromptEngine::new(
        override_dir
            .is_dir()
            .then(|| override_dir.to_string_lossy().to_string()),
    );

    let mut vars = HashMap::new();
    vars.insert(
        "submission_instructions".to_string(),
        submission_instructions(&config.source, &config.label),
    );

    let rendered = engine.render_phase("prd", &vars)?;

    let (binary, args) = build_prd_command(config, &rendered, description);

    info!(
        binary = %binary,
        args = ?args.iter().take(3).collect::<Vec<_>>(),
        "launching interactive PRD session"
    );

    let mut cmd = tokio::process::Command::new(&binary);
    cmd.args(&args)
        .stdin(Stdio::inherit())
        .stdout(Stdio::inherit())
        .stderr(Stdio::inherit());

    // Remove CLAUDECODE env var to allow nested CLI invocation.
    
All rows above produced by Repobility · https://repobility.com
test_submission_instructions_github_custom_label function · rust · L124-L129 (6 LOC)
src/prd.rs
    fn test_submission_instructions_github_custom_label() {
        let instr = submission_instructions("github", "ai-tasks");
        assert!(instr.contains("--label \"ai-tasks\""));
        assert!(instr.contains("ai-tasks"));
        assert!(!instr.contains("rlph"));
    }
test_build_prd_command_basic function · rust · L147-L156 (10 LOC)
src/prd.rs
    fn test_build_prd_command_basic() {
        let config = test_config("claude", "github", None);
        let (cmd, args) = build_prd_command(&config, "rendered prompt", None);
        assert_eq!(cmd, "claude");
        // Template passed as positional arg (last element)
        assert_eq!(args.last().unwrap(), "rendered prompt");
        // No system prompt or -p flags
        assert!(!args.contains(&"--append-system-prompt".to_string()));
        assert!(!args.contains(&"-p".to_string()));
    }
test_build_prd_command_with_description function · rust · L159-L167 (9 LOC)
src/prd.rs
    fn test_build_prd_command_with_description() {
        let config = test_config("claude", "github", None);
        let (_, args) = build_prd_command(&config, "prompt", Some("add auth"));
        let positional = args.last().unwrap();
        assert!(positional.contains("prompt"));
        assert!(positional.contains("## Desired Objective"));
        assert!(positional.contains("add auth"));
        assert!(!args.contains(&"-p".to_string()));
    }
‹ prevpage 2 / 7next ›