fix(desktop): evict stale lane entry when overlay moves session to worktree
When a session's cwd moves from the main checkout to a newly created worktree, overlayRepoLanes places it into the matching worktree lane but never removed the stale entry from the main lane. The session appeared under both groups until the user left and re-entered the project view. Add a cross-lane eviction loop that removes the session from all other lanes before inserting it into the target lane.
This commit is contained in:
@@ -901,6 +901,39 @@ describe('overlayLiveLanes', () => {
|
||||
|
||||
expect(overlayLiveLanes(home, [makeSession('/www/app', { id: 'fresh' })])).toBe(home)
|
||||
})
|
||||
|
||||
it('evicts a session from the main lane when the live overlay places it into a worktree lane', () => {
|
||||
// Session was in main when the backend tree was captured, but the live
|
||||
// $sessions cache now has it under a worktree cwd. The overlay must place
|
||||
// it ONLY in the worktree lane — not both.
|
||||
const session = makeSession('/www/app/.worktrees/feature', { id: 'moved', git_branch: 'feature' })
|
||||
|
||||
const project = projectNode({
|
||||
id: '/www/app',
|
||||
repos: [
|
||||
{
|
||||
id: '/www/app',
|
||||
label: 'app',
|
||||
path: '/www/app',
|
||||
sessionCount: 1,
|
||||
groups: [
|
||||
lane({ id: '/www/app::branch::main', label: 'main', isMain: true, path: '/www/app', sessions: [session] }),
|
||||
lane({ id: '/www/app/.worktrees/feature', label: 'feature', path: '/www/app/.worktrees/feature', sessions: [] })
|
||||
]
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
const overlaid = overlayLiveLanes(project, [session])
|
||||
const mainLane = overlaid.repos[0].groups.find(g => g.isMain)
|
||||
const featureLane = overlaid.repos[0].groups.find(g => g.path === '/www/app/.worktrees/feature')
|
||||
|
||||
// Session must NOT appear in the main lane
|
||||
expect(mainLane?.sessions ?? []).toHaveLength(0)
|
||||
// Session must appear only in the worktree lane
|
||||
expect(featureLane?.sessions.map(s => s.id)).toEqual(['moved'])
|
||||
expect(overlaid.sessionCount).toBe(1)
|
||||
})
|
||||
})
|
||||
|
||||
describe('overlayLivePreviews', () => {
|
||||
|
||||
@@ -586,6 +586,22 @@ export function overlayRepoLanes(
|
||||
}
|
||||
}
|
||||
|
||||
// Evict the session from any OTHER lane the backend snapshot may have
|
||||
// placed it in (e.g. a turn that moved the session's cwd from main to a
|
||||
// new worktree — the overlay places it into the worktree lane, but without
|
||||
// this eviction the stale main-lane entry persists and the session appears
|
||||
// under both groups until the next backend tree refresh).
|
||||
for (const g of lanes) {
|
||||
if (g !== lane) {
|
||||
const idx = g.sessions.findIndex(s => s.id === session.id)
|
||||
|
||||
if (idx >= 0) {
|
||||
g.sessions = [...g.sessions.slice(0, idx), ...g.sessions.slice(idx + 1)]
|
||||
changed = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lane.sessions = upsertSession(lane.sessions, session)
|
||||
changed = true
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user