fix(coder): no-upstream branch alone no longer flags a session at-risk

Session worktree branches (session-<id>) never get an upstream, so the original atRisk rule (unpushed !== 0) flagged every worktree-backed session as at-risk on delete — even pristine ones — forcing a Stash/Force confirm on each. Gate the unpushed arm behind hasUpstream (unpushed !== -1) so the no-upstream sentinel can't trigger it: atRisk = dirty || unmerged > 0 || (hasUpstream && unpushed > 0). No protection is lost — any genuinely unsafe local commit also shows as unmerged > 0 — and the unpushed > 0 arm stays correct for P1.5's pushable worktree branches. unpushed is still reported (-1 = local-only) as informational. Follow-up to 3a26563.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-30 22:19:53 +00:00
parent f69ea5f494
commit c9e302da37

View File

@@ -196,7 +196,7 @@ export interface RiskReport {
dirty: boolean; // uncommitted working-tree changes (incl. untracked) dirty: boolean; // uncommitted working-tree changes (incl. untracked)
unpushed: number; // commits ahead of upstream, or -1 if no upstream is set 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 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 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; 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-<id>) 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 }; return { worktreePath, branch, dirty, unpushed, unmerged, atRisk };
} }