From 8685c55ec9ac48295be4f58488596e0a4fd94de0 Mon Sep 17 00:00:00 2001 From: Matt Toohey Date: Wed, 15 Apr 2026 16:56:17 +1000 Subject: [PATCH 1/2] fix(staged): show "Looking for changes" and fix delayed commits after project-from-PR creation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When creating a project from a PR, users saw an empty branch card with enlarged action buttons before commits eventually appeared. This fixes two issues: 1. Show "Looking for changes…" status during the gap between worktree provisioning completing and the timeline loading, instead of briefly showing nothing or the empty-state enlarged buttons. 2. Ensure timeline data is fresh by invalidating caches when worktree setup completes and before reloading after session completion. Also logs git errors in timeline construction instead of silently swallowing them with unwrap_or_default(). Co-Authored-By: Claude Opus 4.6 (1M context) --- apps/staged/src-tauri/src/timeline.rs | 10 ++++++++-- .../src/lib/features/branches/BranchCard.svelte | 16 +++++++++++++--- .../src/lib/features/projects/ProjectHome.svelte | 1 + 3 files changed, 22 insertions(+), 5 deletions(-) diff --git a/apps/staged/src-tauri/src/timeline.rs b/apps/staged/src-tauri/src/timeline.rs index ac3dbe32c..a0326c784 100644 --- a/apps/staged/src-tauri/src/timeline.rs +++ b/apps/staged/src-tauri/src/timeline.rs @@ -83,8 +83,14 @@ fn build_branch_timeline(store: &Arc, branch_id: &str) -> Result commits, + Err(e) => { + log::warn!("Failed to get commits since base for branch {branch_id}: {e:?}"); + vec![] + } + }; // For each git commit, look up our metadata (session linkage) for gc in git_commits { diff --git a/apps/staged/src/lib/features/branches/BranchCard.svelte b/apps/staged/src/lib/features/branches/BranchCard.svelte index d3c5dc81e..18c1de42f 100644 --- a/apps/staged/src/lib/features/branches/BranchCard.svelte +++ b/apps/staged/src/lib/features/branches/BranchCard.svelte @@ -127,6 +127,11 @@ (isRemote && remoteWorkspaceStatus === 'starting') ); + /** True during provisioning OR the gap between worktree-ready and timeline-loaded. */ + let isSettingUp = $derived( + isProvisioning || (isLocal && !!branch.worktreePath && !timeline && !error) + ); + /** Empty timeline used during provisioning so the action buttons render. */ const emptyTimeline: BranchTimelineData = { commits: [], notes: [], reviews: [], images: [] }; @@ -186,6 +191,10 @@ if (isRemote && remoteWorkspaceStatus === 'starting') { return 'Starting workspace…'; } + // Worktree is ready but timeline hasn't loaded yet + if (isLocal && branch.worktreePath && !timeline && !error) { + return 'Looking for changes…'; + } return undefined; }); @@ -446,6 +455,7 @@ return; } + commands.invalidateBranchTimeline(branch.id); loadTimeline(); // Handle PR session completion if (prButton && eventSessionId === prButton.getPrSessionId()) { @@ -948,7 +958,7 @@ {repoLabel} {isLocal} {isRemote} - {isProvisioning} + isProvisioning={isSettingUp} {remoteWorkspaceStatus} {onDelete} {onRename} @@ -971,7 +981,7 @@ {workspaceError} fallbackError={error} /> - {:else if loading && !isProvisioning} + {:else if loading && !isSettingUp}
Loading... @@ -981,7 +991,7 @@ {error}
- {:else if timeline || isProvisioning} + {:else if timeline || isSettingUp} b.id)); workspaceLifecycle.enqueueInitialSetup(projectId, branches); replaceProjectRepos(projectId, repos); void repoBadgeStore.ensureForRepos( From c27d03e300d6ced0b8f5b1844b7f8a007e3ad5cf Mon Sep 17 00:00:00 2001 From: Matt Toohey Date: Wed, 15 Apr 2026 17:03:06 +1000 Subject: [PATCH 2/2] refactor(staged): derive provisioningLabel/Detail from isSettingUp/isProvisioning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Resolve code review feedback on 8685c55: 1. provisioningLabel and provisioningDetail now derive from the isSettingUp and isProvisioning computed values instead of duplicating their raw conditions, keeping a single source of truth. 2. Rename the isProvisioning prop on BranchCardActionsBar to isSettingUp to match what is actually passed — the broader flag that covers both provisioning and the worktree-ready-but-timeline- not-loaded gap. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../lib/features/branches/BranchCard.svelte | 48 +++++++++---------- .../branches/BranchCardActionsBar.svelte | 8 ++-- 2 files changed, 26 insertions(+), 30 deletions(-) diff --git a/apps/staged/src/lib/features/branches/BranchCard.svelte b/apps/staged/src/lib/features/branches/BranchCard.svelte index 18c1de42f..33fb490ab 100644 --- a/apps/staged/src/lib/features/branches/BranchCard.svelte +++ b/apps/staged/src/lib/features/branches/BranchCard.svelte @@ -176,26 +176,23 @@ /** Label for the provisioning timeline row, if applicable. */ let provisioningLabel = $derived.by(() => { - if (isLocal && !branch.worktreePath && !worktreeError) { - if (setupPhase) { - const labels: Record = { - cloning: 'Cloning repository…', - fetching: 'Fetching latest changes…', - creating_worktree: 'Creating worktree…', - running_setup_actions: 'Running setup actions…', - }; - return labels[setupPhase] ?? 'Setting up…'; + if (!isSettingUp) return undefined; + if (isProvisioning) { + if (isLocal) { + if (setupPhase) { + const labels: Record = { + cloning: 'Cloning repository…', + fetching: 'Fetching latest changes…', + creating_worktree: 'Creating worktree…', + running_setup_actions: 'Running setup actions…', + }; + return labels[setupPhase] ?? 'Setting up…'; + } + return 'Setting up…'; } - return 'Setting up…'; - } - if (isRemote && remoteWorkspaceStatus === 'starting') { return 'Starting workspace…'; } - // Worktree is ready but timeline hasn't loaded yet - if (isLocal && branch.worktreePath && !timeline && !error) { - return 'Looking for changes…'; - } - return undefined; + return 'Looking for changes…'; }); /** Map blox orchestrator CommandType enum names to display labels. */ @@ -208,15 +205,14 @@ /** Detail text for the provisioning row (e.g. git progress percentages or step info). */ let provisioningDetail = $derived.by(() => { - if (isLocal && !branch.worktreePath && !worktreeError) return setupDetail; - if (isRemote && remoteWorkspaceStatus === 'starting') { - if (setupDetail && setupPhase) { - const label = remoteCommandLabels[setupPhase] ?? setupPhase; - return `${setupDetail} · ${label}`; - } - return setupDetail; + if (!isProvisioning) return null; + if (isLocal) return setupDetail; + // Remote workspace starting + if (setupDetail && setupPhase) { + const label = remoteCommandLabels[setupPhase] ?? setupPhase; + return `${setupDetail} · ${label}`; } - return null; + return setupDetail; }); /** True when the branch has at least one finalized commit (code changes vs base). */ @@ -958,7 +954,7 @@ {repoLabel} {isLocal} {isRemote} - isProvisioning={isSettingUp} + {isSettingUp} {remoteWorkspaceStatus} {onDelete} {onRename} diff --git a/apps/staged/src/lib/features/branches/BranchCardActionsBar.svelte b/apps/staged/src/lib/features/branches/BranchCardActionsBar.svelte index 8f2fc7128..fd84d3852 100644 --- a/apps/staged/src/lib/features/branches/BranchCardActionsBar.svelte +++ b/apps/staged/src/lib/features/branches/BranchCardActionsBar.svelte @@ -61,7 +61,7 @@ repoLabel?: ProjectRepo | null; isLocal: boolean; isRemote: boolean; - isProvisioning: boolean; + isSettingUp: boolean; remoteWorkspaceStatus: string | null; onDelete?: () => void; onRename?: (branchName: string) => void; @@ -77,7 +77,7 @@ repoLabel = null, isLocal, isRemote, - isProvisioning, + isSettingUp, remoteWorkspaceStatus, onDelete, onRename, @@ -617,7 +617,7 @@ {/each} - {#if !isProvisioning && primaryRunAction} + {#if !isSettingUp && primaryRunAction} {@const execution = primaryActionExecution} {@const isRunning = execution?.status === 'running'} {@const isStopping = execution && stoppingExecutions.has(execution.executionId)} @@ -755,7 +755,7 @@ {#if showMoreMenu}
- {#if !isProvisioning} + {#if !isSettingUp} {#if isRemote && branch.workspaceName}