diff --git a/apps/coder/src/services/worktrees.ts b/apps/coder/src/services/worktrees.ts index 820635a..aad7ead 100644 --- a/apps/coder/src/services/worktrees.ts +++ b/apps/coder/src/services/worktrees.ts @@ -196,7 +196,7 @@ export interface RiskReport { dirty: boolean; // uncommitted working-tree changes (incl. untracked) unpushed: number; // commits ahead of upstream, or -1 if no upstream is set unmerged: number; // commits on this branch not in the project default branch - atRisk: boolean; // dirty || unpushed !== 0 || unmerged > 0 || git error + atRisk: boolean; // dirty || unmerged > 0 || (upstream && unpushed > 0) || git error error?: string; // populated on a git failure; presence forces atRisk } @@ -308,7 +308,13 @@ export async function checkWorktreeWorkAtRisk( if (rl.exitCode === 0) unmerged = parseInt(rl.stdout.trim() || '0', 10) || 0; } - const atRisk = dirty || unpushed !== 0 || unmerged > 0; + // unpushed only contributes when an upstream actually exists. Session branches + // (session-) never have one (unpushed === -1), and any real local-only work + // there already surfaces as unmerged > 0 — so the no-upstream case adds no + // protection, only friction (it flagged every pristine worktree-backed session). + // The unpushed > 0 arm stays forward-compatible with P1.5 pushable branches. + const hasUpstream = unpushed !== -1; + const atRisk = dirty || unmerged > 0 || (hasUpstream && unpushed > 0); return { worktreePath, branch, dirty, unpushed, unmerged, atRisk }; }