fix: new worktree not sorting to top with sort=recent (#314)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
df43248761
commit
3ec7fcb05e
|
|
@ -354,4 +354,31 @@ describe('buildWorktreeComparator', () => {
|
|||
|
||||
expect(worktrees.map((worktree) => worktree.id)).toEqual(['active', 'background'])
|
||||
})
|
||||
|
||||
it('ranks a just-created worktree above shutdown worktrees with passive signals', () => {
|
||||
const justCreated = makeWorktree({
|
||||
id: 'new',
|
||||
displayName: 'New',
|
||||
lastActivityAt: NOW
|
||||
})
|
||||
// Shutdown worktree with max passive signals but no recent activity
|
||||
const shutdown = makeWorktree({
|
||||
id: 'shutdown',
|
||||
displayName: 'Shutdown',
|
||||
isUnread: true,
|
||||
linkedIssue: 42,
|
||||
lastActivityAt: NOW - 2 * 24 * 60 * 60 * 1000
|
||||
})
|
||||
const prCache = {
|
||||
'/tmp/repo-1::shutdown': {
|
||||
data: { number: 17 },
|
||||
fetchedAt: NOW
|
||||
}
|
||||
}
|
||||
const worktrees = [shutdown, justCreated]
|
||||
|
||||
worktrees.sort(buildWorktreeComparator('recent', null, repoMap, prCache, NOW))
|
||||
|
||||
expect(worktrees.map((worktree) => worktree.id)).toEqual(['new', 'shutdown'])
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -73,7 +73,12 @@ function computeSmartScoreFromSignals(
|
|||
const activityAge = now - (worktree.lastActivityAt || 0)
|
||||
if (worktree.lastActivityAt > 0) {
|
||||
const ONE_DAY = 24 * 60 * 60 * 1000
|
||||
score += 24 * Math.max(0, 1 - activityAge / ONE_DAY)
|
||||
// Why 36: a just-created worktree has only this signal (no live tab yet,
|
||||
// since the PTY spawns asynchronously after creation). Weight must exceed
|
||||
// the max passive-signal combination for shutdown worktrees
|
||||
// (isUnread 18 + PR 10 + issue 6 = 34) so brand-new worktrees always
|
||||
// appear at the top of the "recent" sort immediately.
|
||||
score += 36 * Math.max(0, 1 - activityAge / ONE_DAY)
|
||||
}
|
||||
|
||||
return score
|
||||
|
|
@ -161,12 +166,12 @@ export function buildWorktreeComparator(
|
|||
*
|
||||
* Scoring:
|
||||
* running AI job → +60
|
||||
* recent activity → +36 (decays over 24 hours)
|
||||
* needs attention → +35
|
||||
* unread → +18
|
||||
* open terminal → +12
|
||||
* live branch PR → +10
|
||||
* linked issue → +6
|
||||
* recent activity → +24 (decays over 24 hours)
|
||||
*/
|
||||
export function computeSmartScore(
|
||||
worktree: Worktree,
|
||||
|
|
|
|||
|
|
@ -231,12 +231,22 @@ export const createTerminalSlice: StateCreator<AppState, [], [], TerminalSlice>
|
|||
next[wId] = next[wId].map((t) => (t.id === tabId ? { ...t, ptyId } : t))
|
||||
}
|
||||
const existingPtyIds = s.ptyIdsByTabId[tabId] ?? []
|
||||
// Why: when a brand-new tab in the active worktree receives its first
|
||||
// PTY, the live-tab signal (+12) flips on. bumpWorktreeActivity (below)
|
||||
// intentionally skips sortEpoch for the active worktree to prevent the
|
||||
// reorder-on-click bug (PR #209), but that means the sort never sees
|
||||
// the new signal. Bump sortEpoch here so a just-created worktree
|
||||
// immediately reflects its live-tab score instead of waiting for an
|
||||
// unrelated event to trigger a re-sort.
|
||||
const isFirstPty = existingPtyIds.length === 0
|
||||
const isActiveWorktree = worktreeId != null && s.activeWorktreeId === worktreeId
|
||||
return {
|
||||
tabsByWorktree: next,
|
||||
ptyIdsByTabId: {
|
||||
...s.ptyIdsByTabId,
|
||||
[tabId]: existingPtyIds.includes(ptyId) ? existingPtyIds : [...existingPtyIds, ptyId]
|
||||
}
|
||||
},
|
||||
...(isFirstPty && isActiveWorktree ? { sortEpoch: s.sortEpoch + 1 } : {})
|
||||
}
|
||||
})
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue