Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
97 changes: 91 additions & 6 deletions apps/staged/src-tauri/src/branches.rs
Original file line number Diff line number Diff line change
Expand Up @@ -337,10 +337,31 @@ async fn clone_repo_into_workspace(
"Failed to fetch base branch '{base_ref}' for '{repo_slug}' in workspace '{ws_name}': {e}"
))?;

// If the branch already exists on the remote (e.g. from an existing PR),
// fetch it and start from there so we pick up its commits. Otherwise fall
// back to starting from the base branch.
let has_remote_branch = if branch_name != base_ref {
run_workspace_git_async(
ws_name,
Some(repo_subpath),
&["fetch", "origin", branch_name],
)
.await
.is_ok()
} else {
false
};

let start_point = if has_remote_branch {
format!("origin/{branch_name}")
} else {
format!("origin/{base_ref}")
};

run_workspace_git_async(
ws_name,
Some(repo_subpath),
&["checkout", "-B", branch_name, &format!("origin/{base_ref}")],
&["checkout", "-B", branch_name, &start_point],
)
.await
.map_err(|e| {
Expand Down Expand Up @@ -1026,6 +1047,7 @@ pub async fn create_remote_branch(
#[tauri::command(rename_all = "camelCase")]
pub async fn start_workspace(
store: tauri::State<'_, Mutex<Option<Arc<Store>>>>,
app_handle: AppHandle,
branch_id: String,
) -> Result<(), String> {
let store = get_store(&store)?;
Expand All @@ -1035,6 +1057,12 @@ pub async fn start_workspace(
.map_err(|e| e.to_string())?
.ok_or_else(|| format!("Branch not found: {branch_id}"))?;

// Track whether this is the first workspace start (Starting → Running)
// vs a restart (Stopped → Running). We only trigger auto-review on the
// first start so that existing branches don't get a spurious review
// every time the workspace restarts.
let is_first_start = branch.workspace_status == Some(store::WorkspaceStatus::Starting);

let project = store
.get_project(&branch.project_id)
.map_err(|e| e.to_string())?
Expand Down Expand Up @@ -1088,6 +1116,24 @@ pub async fn start_workspace(
store
.update_branch_workspace_status(&branch_id, &store::WorkspaceStatus::Running)
.map_err(|e| e.to_string())?;

// Trigger auto-review for the newly cloned secondary repo
// if this is the first start for this branch.
if is_first_start {
let store_bg = Arc::clone(&store);
let app_handle_bg = app_handle.clone();
let branch_id_bg = branch_id.clone();
tauri::async_runtime::spawn(async move {
crate::maybe_trigger_auto_review_for_new_repo(
&store_bg,
&app_handle_bg,
&branch_id_bg,
None,
)
.await;
});
}

return Ok(());
}
}
Expand Down Expand Up @@ -1124,21 +1170,60 @@ pub async fn start_workspace(
ws_name,
ws_start_started_at.elapsed().as_millis()
);
// Create the feature branch inside the workspace so work happens
// on `branch_name` rather than the detached base ref.
if let Err(e) = run_workspace_git_async(
// If the branch already exists on the remote (e.g. from an
// existing PR), fetch it and start from there so we pick up its
// commits. Otherwise create a fresh branch from the base ref.
let has_remote_branch = run_workspace_git_async(
ws_name,
repo_subpath.as_deref(),
&["checkout", "-b", &branch.branch_name],
&["fetch", "origin", &branch.branch_name],
)
.await
{
.is_ok();

let remote_ref = format!("origin/{}", branch.branch_name);
let checkout_result = if has_remote_branch {
run_workspace_git_async(
ws_name,
repo_subpath.as_deref(),
&["checkout", "-B", &branch.branch_name, &remote_ref],
)
.await
} else {
run_workspace_git_async(
ws_name,
repo_subpath.as_deref(),
&["checkout", "-b", &branch.branch_name],
)
.await
};

if let Err(e) = checkout_result {
log::warn!(
"failed to create branch '{}' in workspace '{}': {e}",
branch.branch_name,
ws_name
);
}

// If this is the first workspace start for a new branch, check
// whether the branch already has commits (e.g. from an existing
// PR) and kick off an automatic code review.
if is_first_start {
let store_bg = Arc::clone(&store);
let app_handle_bg = app_handle.clone();
let branch_id_bg = branch_id.clone();
tauri::async_runtime::spawn(async move {
crate::maybe_trigger_auto_review_for_new_repo(
&store_bg,
&app_handle_bg,
&branch_id_bg,
None,
)
.await;
});
}

Ok(())
}
Err(blox::BloxError::NotAuthenticated) => {
Expand Down
119 changes: 110 additions & 9 deletions apps/staged/src-tauri/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,69 @@ fn confirm_reset_store(
// Project commands
// =============================================================================

/// Check whether a newly added repo already has commits on its branch and, if
/// so, kick off an automatic code review.
///
/// For **local** branches (`worktree_path: Some`) the check is done by
/// inspecting the worktree on disk before triggering.
/// For **remote** branches (`worktree_path: None`) the review is triggered
/// unconditionally — the caller is responsible for ensuring the workspace is
/// running before calling this (e.g. after `start_workspace` or
/// `setup_remote_repo_clone` completes).
///
/// This is fire-and-forget — errors are logged but never propagated.
pub(crate) async fn maybe_trigger_auto_review_for_new_repo(
store: &Arc<Store>,
app_handle: &tauri::AppHandle,
branch_id: &str,
worktree_path: Option<&str>,
) {
// For local branches, check whether there are any commits on the branch
// relative to its base before spinning up a review session.
if let Some(path) = worktree_path {
let branch = match store.get_branch(branch_id) {
Ok(Some(b)) => b,
_ => return,
};
let worktree = std::path::PathBuf::from(path);
match git::get_commits_since_base(&worktree, &branch.base_branch) {
Ok(commits) if commits.is_empty() => {
log::info!(
"[auto_review] branch {branch_id} has no commits yet — skipping auto review"
);
return;
}
Err(e) => {
log::warn!("[auto_review] failed to check commits for branch {branch_id}: {e}");
return;
}
Ok(_) => { /* has commits — fall through to trigger */ }
}
}

let registry = app_handle.state::<Arc<session_runner::SessionRegistry>>();
match session_commands::trigger_auto_review(
Arc::clone(store),
Arc::clone(&registry),
app_handle.clone(),
Comment on lines +252 to +256

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Gate remote auto-review on branch commits

When worktree_path is None (the remote-branch path), this helper skips any commit check and always calls trigger_auto_review, so a brand-new remote branch created from base with zero branch commits still starts an automatic review session. All new remote callsites in this commit pass None, so adding/starting remote repos without existing commits now triggers unnecessary reviews and token spend instead of only reviewing pre-existing branch work.

Useful? React with 👍 / 👎.

branch_id.to_string(),
None,
)
.await
{
Ok(resp) => {
log::info!(
"[auto_review] triggered for new repo on branch {branch_id}: session={}, review={}",
resp.session_id,
resp.artifact_id,
);
}
Err(e) => {
log::warn!("[auto_review] failed to trigger for branch {branch_id}: {e}");
}
}
}

#[tauri::command]
fn list_projects(
store: tauri::State<'_, Mutex<Option<Arc<Store>>>>,
Expand Down Expand Up @@ -298,7 +361,7 @@ fn create_project(
format!("origin/{detected_base}")
};

let branch_id = match project.location {
let (branch_id, is_local) = match project.location {
store::ProjectLocation::Local => {
let mut branch =
store::Branch::new(&project.id, &inferred_branch_name, &effective_base)
Expand All @@ -307,7 +370,7 @@ fn create_project(
branch = branch.with_pr(pr);
}
store.create_branch(&branch).map_err(|e| e.to_string())?;
Some(branch.id)
(branch.id, true)
}
store::ProjectLocation::Remote => {
let workspace_name = branches::infer_workspace_name(&inferred_branch_name);
Expand All @@ -328,12 +391,12 @@ fn create_project(
workspace_name,
project.id
);
None // remote branches don't need worktree setup
(branch.id, false)
}
};

// Spawn background worktree + prerun-actions setup for local branches.
if let Some(branch_id) = branch_id {
if is_local {
// Spawn background worktree + prerun-actions setup for local branches.
let project_id = project.id.clone();
let store_bg = Arc::clone(&store);
tauri::async_runtime::spawn(async move {
Expand All @@ -346,10 +409,11 @@ fn create_project(
})
.await;

match worktree_result {
let worktree_path = match worktree_result {
Ok(Ok(path)) => {
log::info!("[create_project] worktree ready at {path}");
let _ = app_handle.emit("project-setup-progress", project_id.clone());
path
}
Ok(Err(e)) => {
log::warn!("[create_project] worktree setup failed: {e}");
Expand All @@ -359,7 +423,7 @@ fn create_project(
log::warn!("[create_project] worktree task panicked: {e}");
return;
}
}
};

let executor = app_handle.state::<Arc<actions::ActionExecutor>>();
let act_registry = app_handle.state::<Arc<actions::ActionRegistry>>();
Expand All @@ -380,7 +444,21 @@ fn create_project(
log::warn!("[create_project] prerun actions failed: {e}");
}
}

// If the repo already has commits on this branch, kick off
// an automatic code review so the user gets immediate feedback.
maybe_trigger_auto_review_for_new_repo(
&store_bg,
&app_handle,
&branch_id,
Some(&worktree_path),
)
.await;
});
} else {
// Remote branches: auto-review is deferred until `start_workspace`
// completes — the workspace isn't running yet at this point, so
// attempting to trigger a review here would fail.
}
} else if project.location == store::ProjectLocation::Remote {
log::info!(
Expand Down Expand Up @@ -472,10 +550,11 @@ async fn add_project_repo(
})
.await;

match worktree_result {
let worktree_path = match worktree_result {
Ok(Ok(path)) => {
log::info!("[add_project_repo] worktree ready at {path}");
let _ = app_handle.emit("project-setup-progress", project_id.clone());
path
}
Ok(Err(e)) => {
log::warn!("[add_project_repo] worktree setup failed: {e}");
Expand All @@ -485,7 +564,7 @@ async fn add_project_repo(
log::warn!("[add_project_repo] worktree task panicked: {e}");
return;
}
}
};

let executor = app_handle.state::<Arc<actions::ActionExecutor>>();
let act_registry = app_handle.state::<Arc<actions::ActionRegistry>>();
Expand All @@ -506,6 +585,16 @@ async fn add_project_repo(
log::warn!("[add_project_repo] prerun actions failed: {e}");
}
}

// If the repo already has commits on this branch, kick off
// an automatic code review so the user gets immediate feedback.
maybe_trigger_auto_review_for_new_repo(
&store,
&app_handle,
&branch.id,
Some(&worktree_path),
)
.await;
} else {
// Remote branch: clone the repo into the running workspace,
// fetch the base branch, and create the feature branch.
Expand All @@ -521,9 +610,21 @@ async fn add_project_repo(
"[add_project_repo] remote repo clone failed for branch '{}': {e}",
branch.branch_name
);
let _ = app_handle.emit("project-setup-progress", project_id);
return;
}
}
let _ = app_handle.emit("project-setup-progress", project_id);

// If the repo already has commits on this branch, kick off
// an automatic code review so the user gets immediate feedback.
maybe_trigger_auto_review_for_new_repo(
&store,
&app_handle,
&branch.id,
None, // remote — trigger_auto_review resolves HEAD via workspace
)
.await;
}
}
});
Expand Down
Loading