From 3282af1d3b4c248c326e8e523573bdddaf6738e1 Mon Sep 17 00:00:00 2001 From: Matt Toohey Date: Thu, 19 Feb 2026 14:06:50 -0800 Subject: [PATCH 1/2] fix: use per-repo context for actions and session workdir in multi-repo projects When a project has multiple repos, branch cards were all showing the actions of the primary repo because list_project_actions ignored the branch's project_repo_id. Now passes projectRepoId from the frontend so each branch gets the action context for its own repo. Session creation was also failing for branches on secondary repos because start_branch_session always appended project.subpath to the worktree path. For branches linked to a project_repo, the repo's own subpath (which may be null) is now used instead. Co-Authored-By: Claude Sonnet 4.6 --- staged/src-tauri/src/lib.rs | 15 +++++++++++++-- staged/src-tauri/src/session_commands.rs | 14 +++++++++++++- staged/src/lib/commands.ts | 7 +++++-- .../src/lib/features/branches/BranchCard.svelte | 2 +- 4 files changed, 32 insertions(+), 6 deletions(-) diff --git a/staged/src-tauri/src/lib.rs b/staged/src-tauri/src/lib.rs index 217aaa4b..b6fc2dcd 100644 --- a/staged/src-tauri/src/lib.rs +++ b/staged/src-tauri/src/lib.rs @@ -696,13 +696,24 @@ fn get_or_create_project_action_context( .map_err(|e| e.to_string()) } -#[tauri::command] +#[tauri::command(rename_all = "camelCase")] fn list_project_actions( store: tauri::State<'_, Mutex>>>, project_id: String, + project_repo_id: Option, ) -> Result, String> { let store = get_store(&store)?; - let context = get_or_create_project_action_context(&store, &project_id)?; + let context = if let Some(repo_id) = project_repo_id { + let repo = store + .get_project_repo(&repo_id) + .map_err(|e| e.to_string())? + .ok_or_else(|| format!("Project repo not found: {repo_id}"))?; + store + .get_or_create_action_context(&repo.github_repo, repo.subpath.as_deref()) + .map_err(|e| e.to_string())? + } else { + get_or_create_project_action_context(&store, &project_id)? + }; store .list_repo_actions(&context.id) .map_err(|e| e.to_string()) diff --git a/staged/src-tauri/src/session_commands.rs b/staged/src-tauri/src/session_commands.rs index cd2c58b7..5742897b 100644 --- a/staged/src-tauri/src/session_commands.rs +++ b/staged/src-tauri/src/session_commands.rs @@ -353,7 +353,19 @@ pub async fn start_branch_session( .ok_or_else(|| format!("No worktree for branch: {branch_id}"))?; let mut worktree_path = PathBuf::from(&workdir.path); - if let Some(ref subpath) = project.subpath { + // Use the project_repo's subpath when the branch is attached to a specific + // repo (e.g. a secondary repo with no subpath), rather than always falling + // back to the project-level subpath which may belong to a different repo. + let effective_subpath = if let Some(repo_id) = branch.project_repo_id.as_deref() { + store + .get_project_repo(repo_id) + .ok() + .flatten() + .and_then(|repo| repo.subpath) + } else { + project.subpath.clone() + }; + if let Some(ref subpath) = effective_subpath { worktree_path = worktree_path.join(subpath); } diff --git a/staged/src/lib/commands.ts b/staged/src/lib/commands.ts index 40dd2f7c..2a307dc8 100644 --- a/staged/src/lib/commands.ts +++ b/staged/src/lib/commands.ts @@ -307,8 +307,11 @@ export interface ProjectAction { updatedAt: number; } -export function listProjectActions(projectId: string): Promise { - return invoke('list_project_actions', { projectId }); +export function listProjectActions( + projectId: string, + projectRepoId?: string | null +): Promise { + return invoke('list_project_actions', { projectId, projectRepoId: projectRepoId ?? null }); } export function createProjectAction( diff --git a/staged/src/lib/features/branches/BranchCard.svelte b/staged/src/lib/features/branches/BranchCard.svelte index 661f1900..468e83df 100644 --- a/staged/src/lib/features/branches/BranchCard.svelte +++ b/staged/src/lib/features/branches/BranchCard.svelte @@ -577,7 +577,7 @@ async function loadActions() { try { // Load actions for this branch's project - actions = await commands.listProjectActions(branch.projectId); + actions = await commands.listProjectActions(branch.projectId, branch.projectRepoId); } catch (e) { console.error('Failed to load actions:', e); actions = []; From 40721be048353c3e0b51b3e3e9f50d546e1c78b4 Mon Sep 17 00:00:00 2001 From: Matt Toohey Date: Thu, 19 Feb 2026 14:23:07 -0800 Subject: [PATCH 2/2] refactor: remove stale project-scoped action commands superseded by repo-scoped versions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit detect_project_actions and create_project_action always used the project's primary repo, making them incorrect for multi-repo projects. Neither was called from the UI — the action management modal already used the repo-specific detect_repo_actions and create_repo_action commands. Remove the stale backend commands and their frontend wrappers. Co-Authored-By: Claude Sonnet 4.6 --- staged/src-tauri/src/actions/commands.rs | 54 ---------------------- staged/src-tauri/src/lib.rs | 24 ---------- staged/src/lib/commands.ts | 18 -------- staged/src/lib/features/actions/actions.ts | 8 ---- 4 files changed, 104 deletions(-) diff --git a/staged/src-tauri/src/actions/commands.rs b/staged/src-tauri/src/actions/commands.rs index 113e3c30..0c27ecaf 100644 --- a/staged/src-tauri/src/actions/commands.rs +++ b/staged/src-tauri/src/actions/commands.rs @@ -91,60 +91,6 @@ fn resolve_branch_repo_context( Ok((repo.to_string(), project.subpath.clone())) } -/// Detect available actions from a project's build files using AI. -/// -/// If a local clone already exists on disk we read files from the filesystem. -/// Otherwise we use the GitHub API (via `gh`) to inspect the repository -/// remotely, avoiding an expensive clone just for action detection. -#[tauri::command] -pub async fn detect_project_actions( - project_id: String, - app: AppHandle, - store: State<'_, Mutex>>>, -) -> Result, String> { - let store = get_store(&store)?; - - // Get the project - let project = store - .get_project(&project_id) - .map_err(|e| format!("Failed to get project: {e}"))? - .ok_or_else(|| "Project not found".to_string())?; - let github_repo = project - .primary_repo() - .ok_or_else(|| "Project has no repository attached".to_string())?; - - let context = store - .get_or_create_action_context(github_repo, project.subpath.as_deref()) - .map_err(|e| format!("Failed to get action context: {e}"))?; - store - .set_action_context_detecting(&context.id, true) - .map_err(|e| format!("Failed to set detection status: {e}"))?; - let _ = app.emit( - "repo-actions-detection", - DetectingActionsEvent { - github_repo: github_repo.to_string(), - subpath: project.subpath.clone(), - detecting: true, - }, - ); - - let result = detect_actions_for_repo_context(github_repo, project.subpath.as_deref()).await; - - store - .mark_action_context_detected(&context.id) - .map_err(|e| format!("Failed to update detection status: {e}"))?; - let _ = app.emit( - "repo-actions-detection", - DetectingActionsEvent { - github_repo: github_repo.to_string(), - subpath: project.subpath.clone(), - detecting: false, - }, - ); - - result -} - /// Detect available actions for a specific repo+subpath context using AI. #[tauri::command(rename_all = "camelCase")] pub async fn detect_repo_actions( diff --git a/staged/src-tauri/src/lib.rs b/staged/src-tauri/src/lib.rs index b6fc2dcd..8c06d6b1 100644 --- a/staged/src-tauri/src/lib.rs +++ b/staged/src-tauri/src/lib.rs @@ -719,28 +719,6 @@ fn list_project_actions( .map_err(|e| e.to_string()) } -#[tauri::command(rename_all = "camelCase")] -fn create_project_action( - store: tauri::State<'_, Mutex>>>, - project_id: String, - name: String, - command: String, - action_type: String, - sort_order: i32, - auto_commit: bool, -) -> Result { - let store = get_store(&store)?; - let context = get_or_create_project_action_context(&store, &project_id)?; - let parsed_type = builderbot_actions::ActionType::parse(&action_type) - .ok_or_else(|| format!("Invalid action type: {action_type}"))?; - let action = store::models::RepoAction::new(context.id, name, command, parsed_type, sort_order) - .with_auto_commit(auto_commit); - store - .create_repo_action(&action) - .map_err(|e| e.to_string())?; - Ok(action) -} - #[tauri::command(rename_all = "camelCase")] fn update_project_action( store: tauri::State<'_, Mutex>>>, @@ -2360,7 +2338,6 @@ pub fn run() { branches::get_workspace_info, branches::poll_workspace_status, list_project_actions, - create_project_action, update_project_action, delete_project_action, list_action_contexts, @@ -2401,7 +2378,6 @@ pub fn run() { session_commands::delete_session, session_commands::start_branch_session, // Actions - actions::commands::detect_project_actions, actions::commands::detect_repo_actions, actions::commands::run_branch_action, actions::commands::stop_branch_action, diff --git a/staged/src/lib/commands.ts b/staged/src/lib/commands.ts index 2a307dc8..f1bc82e1 100644 --- a/staged/src/lib/commands.ts +++ b/staged/src/lib/commands.ts @@ -314,24 +314,6 @@ export function listProjectActions( return invoke('list_project_actions', { projectId, projectRepoId: projectRepoId ?? null }); } -export function createProjectAction( - projectId: string, - name: string, - command: string, - actionType: string, - sortOrder: number, - autoCommit: boolean -): Promise { - return invoke('create_project_action', { - projectId, - name, - command, - actionType, - sortOrder, - autoCommit, - }); -} - export function updateProjectAction( actionId: string, name: string, diff --git a/staged/src/lib/features/actions/actions.ts b/staged/src/lib/features/actions/actions.ts index 6b08d5b9..974ea181 100644 --- a/staged/src/lib/features/actions/actions.ts +++ b/staged/src/lib/features/actions/actions.ts @@ -86,14 +86,6 @@ export interface RunningActionInfo { startedAt: number; } -/** - * Detect available actions from a project's build files using AI. - * Scans package.json, justfile, Makefile, etc. and suggests relevant actions. - */ -export function detectProjectActions(projectId: string): Promise { - return invoke('detect_project_actions', { projectId }); -} - /** Detect available actions for a repo+subpath context. */ export function detectRepoActions( githubRepo: string,