From 103e9acc7d3e02be7920234478f7e65a3eb94c3c Mon Sep 17 00:00:00 2001 From: Neil <4138956+nwparker@users.noreply.github.com> Date: Sat, 25 Jul 2026 21:55:16 -0700 Subject: [PATCH] test(sidebar): pin the bounded false positive spinner attribution accepts (#9040) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit launchAgent usually clears when the agent exits — clearTabLaunchAgent fires from use-tab-agent.ts once resolveLaunchedAgentExitEvidence proves it via OSC 133;D shell-foreground, a vanished local agent signal, or a completion hook. But both signal paths are gated on !isRemote and a hookless remote agent has no completion hook, so its launch identity can outlive it. A later non-agent process in that tab whose title carries an ora-style braille spinner (pnpm, vite, cargo) then reads as working. That is the trade-off #9647 already made for the sidebar row; this makes the dot match rather than diverge. Pin it with its two bounds — no launch identity cannot spin, and a dead PTY drops out — so it stays a deliberate choice under review instead of drifting into an unexamined default. Also drop an overstated comment: the gate stops a spinner in a tab no agent was launched in, not every frozen spinner frame. Co-authored-by: Orca --- ...rktree-status-spinner-launch-agent.test.ts | 26 ++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/src/renderer/src/lib/worktree-status-spinner-launch-agent.test.ts b/src/renderer/src/lib/worktree-status-spinner-launch-agent.test.ts index d9deef24121..34bc7916164 100644 --- a/src/renderer/src/lib/worktree-status-spinner-launch-agent.test.ts +++ b/src/renderer/src/lib/worktree-status-spinner-launch-agent.test.ts @@ -56,7 +56,7 @@ describe('#9040 worktree dot attributes spinner titles to the launched agent', ( }) // Why: pins the #9647 gate — spinner attribution needs a launch identity, so a - // frozen spinner frame left by an exited agent still cannot spin the dot forever. + // spinner in a tab no agent was launched in cannot spin the dot. it('stays active for a spinner title with no launch identity', () => { const status = getWorktreeStatus( [{ id: 'tab-1', title: '⠐ Review branch for regressions' }], @@ -78,6 +78,30 @@ describe('#9040 worktree dot attributes spinner titles to the launched agent', ( }) }) +// Why: launchAgent usually clears on exit (clearTabLaunchAgent via +// resolveLaunchedAgentExitEvidence), but not on every path — a hookless remote agent has +// no completion hook and no shell-foreground producer, so its launch identity can outlive +// it and a later ora-style spinner then reads as working. Documented trade-off, not an +// oversight — the row builder has behaved this way since #9647. Pinned so it stays a +// deliberate choice rather than drifting silently. +describe('#9040 spinner attribution trade-off is bounded', () => { + it('over-reports a non-agent spinner in a tab an agent was launched in', () => { + const tab = { + id: 'tab-1', + title: '⠋ Progress: resolved 42', + launchAgent: 'claude' + } satisfies Partial + + expect(getWorktreeStatus([tab], [], livePtyMap('tab-1'))).toBe('working') + // Bound 1: the same title cannot spin a tab with no launch identity. + expect(getWorktreeStatus([{ id: 'tab-1', title: tab.title }], [], livePtyMap('tab-1'))).toBe( + 'active' + ) + // Bound 2: a dead PTY drops out regardless of launch identity. + expect(getWorktreeStatus([tab], [], {})).toBe('inactive') + }) +}) + // Why: the gate's stated purpose is that the dot never spins with no matching sidebar // row. Claude's spinner title must reach the same dot/row agreement a named provider // already gets — no better, and no worse.