Track container-only tokens and tab focus for cmd+j ranking

Previously ranked by whether any container-only matches existed (boolean);
now counts tokens matching only containers for finer-grained ranking. Tab
focus recency is now tracked explicitly so recent refocuses rank above
stale worktree activity. Preserves worktree grouping by input order while
applying focused-group MRU within each block.
This commit is contained in:
Jinjing
2026-08-24 09:02:30 -07:00
parent f5fd7303ab
commit 52c5675da0
12 changed files with 211 additions and 30 deletions
@@ -12,7 +12,7 @@ import type { PaletteDocumentRank } from './palette-match/palette-document'
function rank(overrides: Partial<PaletteDocumentRank> = {}): PaletteDocumentRank {
return {
exactIntent: 1,
matchedDirectField: 0,
containerOnlyTokenCount: 0,
wholeQuery: 3,
worstQuality: 5,
usesSupportingEvidence: 0,
@@ -163,8 +163,10 @@ function rankAssignments(args: {
let worstQuality: PaletteMatchQuality = 'field-exact'
let fuzzyTokenCount = 0
const fields = new Set<string>()
// Derive from field metadata only: an evidence field can itself be a container.
let hasDirectHit = false
let containerOnlyTokenCount = 0
let tokenIndex = -1
let tokenHasDirectField = false
let matchedTokenCount = 0
for (const assignment of args.assignments) {
if (paletteMatchQualityRank(assignment.quality) > paletteMatchQualityRank(worstQuality)) {
@@ -174,20 +176,32 @@ function rankAssignments(args: {
fuzzyTokenCount += 1
}
fields.add(assignment.fieldId)
if (assignment.tokenIndex !== tokenIndex) {
if (tokenIndex !== -1 && !tokenHasDirectField) {
containerOnlyTokenCount += 1
}
tokenIndex = assignment.tokenIndex
tokenHasDirectField = false
matchedTokenCount += 1
}
const field = args.document.fieldById.get(assignment.fieldId)
if (field && !field.isContainer) {
hasDirectHit = true
tokenHasDirectField = true
}
}
const isContainerOnly = !hasDirectHit
if (tokenIndex !== -1 && !tokenHasDirectField) {
containerOnlyTokenCount += 1
}
const isContainerOnly =
containerOnlyTokenCount > 0 && containerOnlyTokenCount === matchedTokenCount
return {
worstQuality,
isContainerOnly,
rank: {
exactIntent: args.exactIntent ? 0 : 1,
matchedDirectField: isContainerOnly ? 1 : 0,
containerOnlyTokenCount,
wholeQuery: args.wholeQuery,
worstQuality: paletteMatchQualityRank(worstQuality),
usesSupportingEvidence: args.usesEvidence ? 1 : 0,
@@ -129,8 +129,8 @@ export type PaletteSupportingEvidence = {
export type PaletteDocumentRank = {
/** 0 when a recognized exact intent (such as a task URL) produced this row. */
exactIntent: number
/** 0 when query matched at least one direct field; 1 when all matched tokens are container-only. */
matchedDirectField: number
/** Tokens whose chosen assignment only matched container fields. */
containerOnlyTokenCount: number
/** 0 equality, 1 prefix, 2 word boundary, 3 none — whole query in visible text. */
wholeQuery: number
worstQuality: number
@@ -150,7 +150,7 @@ export type PaletteDocumentMatch = {
const RANK_KEYS: readonly (keyof PaletteDocumentRank)[] = [
'exactIntent',
'matchedDirectField',
'containerOnlyTokenCount',
'wholeQuery',
'worstQuality',
'usesSupportingEvidence',
@@ -2,7 +2,11 @@ import { describe, expect, it } from 'vitest'
import { matchPaletteDocument } from './match-document'
import { mapNormalizedRange, normalizePaletteText } from './normalized-text'
import { preparePaletteQuery, PALETTE_QUERY_MAX_TOKENS } from './palette-query'
import { buildPaletteDocument, type PaletteDocumentInput } from './palette-document'
import {
buildPaletteDocument,
comparePaletteDocumentRank,
type PaletteDocumentInput
} from './palette-document'
import { segmentPaletteText } from './text-segments'
import { isWithinOnePaletteEdit } from './typo-distance'
@@ -383,7 +387,7 @@ describe('typo distance', () => {
})
describe('container field matching', () => {
it('marks matchedDirectField as 1 and demotes quality class when all tokens land on container fields', () => {
it('counts a container-only token and demotes quality class when every token lands on containers', () => {
const tabDoc: PaletteDocumentInput = {
id: 'tab-1',
visibleFields: [
@@ -394,11 +398,11 @@ describe('container field matching', () => {
}
const match = run(tabDoc, '4360')
expect(match).not.toBeNull()
expect(match?.rank.matchedDirectField).toBe(1)
expect(match?.rank.containerOnlyTokenCount).toBe(1)
expect(match?.qualityClass).toBe('exact-evidence')
})
it('marks matchedDirectField as 0 when at least one token lands on a direct field', () => {
it('does not count a token that lands on a direct field', () => {
const tabDoc: PaletteDocumentInput = {
id: 'tab-1',
visibleFields: [
@@ -409,7 +413,40 @@ describe('container field matching', () => {
}
const match = run(tabDoc, '4360')
expect(match).not.toBeNull()
expect(match?.rank.matchedDirectField).toBe(0)
expect(match?.rank.containerOnlyTokenCount).toBe(0)
expect(match?.qualityClass).toBe('exact-visible')
})
it('ranks an all-direct multi-token match ahead of a mixed direct and container match', () => {
const direct = run(
{
id: 'direct',
visibleFields: [
{ id: 'title', profile: 'structured-label', text: 'alpha' },
{ id: 'path', profile: 'structured-label', text: 'beta' }
],
evidence: []
},
'alpha beta'
)
const mixed = run(
{
id: 'mixed',
visibleFields: [
{ id: 'title', profile: 'structured-label', text: 'alpha' },
{ id: 'worktree', profile: 'structured-label', text: 'beta', isContainer: true }
],
evidence: []
},
'alpha beta'
)
expect(direct).not.toBeNull()
expect(mixed).not.toBeNull()
expect(direct?.rank.containerOnlyTokenCount).toBe(0)
expect(mixed?.rank.containerOnlyTokenCount).toBe(1)
if (direct && mixed) {
expect(comparePaletteDocumentRank(direct.rank, mixed.rank)).toBeLessThan(0)
}
})
})
@@ -179,6 +179,24 @@ describe('orderRecentWorkspaceTabs', () => {
).toEqual(['alpha-2', 'alpha-1', 'beta-1'])
})
it('keeps an interleaved worktree block together before applying its focused-group MRU', () => {
const rows = [
row('alpha-old', { worktreeId: 'wt-alpha', unifiedTabId: 'unified-alpha-old' }),
row('beta', { worktreeId: 'wt-beta', unifiedTabId: 'unified-beta' }),
row('alpha-new', { worktreeId: 'wt-alpha', unifiedTabId: 'unified-alpha-new' })
]
expect(
order(rows, sources([]), {
focusedGroupTabRecency: new Map([
['unified-alpha-old', 0],
['unified-alpha-new', 1],
['unified-beta', 0]
])
})
).toEqual(['alpha-new', 'alpha-old', 'beta'])
})
it('keeps input (positional) order when nothing else separates two rows', () => {
const rows = [row('a', { worktreeId: 'wt-1' }), row('b', { worktreeId: 'wt-1' })]
@@ -45,6 +45,7 @@ type RankedRow = {
visitedAt: number | undefined
focusOrdinal: number
worktreeId: string
worktreeOrder: number
}
/** Classes 1 (blocked/waiting) and 2 (freshly done) are the rows that want the user. */
@@ -127,10 +128,8 @@ function compareRankedRows(a: RankedRow, b: RankedRow): number {
}
return b.visitedAt - a.visitedAt
}
// Why: focusOrdinal is a per-worktree sequence, so comparing it across worktrees interleaves
// them arbitrarily. Keep input (positional) order instead.
if (a.worktreeId !== b.worktreeId) {
return 0
if (a.worktreeOrder !== b.worktreeOrder) {
return a.worktreeOrder - b.worktreeOrder
}
return b.focusOrdinal - a.focusOrdinal
}
@@ -139,10 +138,16 @@ function compareRankedRows(a: RankedRow, b: RankedRow): number {
* Rank rows into ids, most-wanted first:
* tier 1 — needs attention: class 1 (blocked/waiting) then 2 (fresh done), newest first
* tier 2 — everything else: worktree focus recency, then focused-group MRU
* Full ties keep input order, which callers pass in positional (worktree/group/tab) order.
* Equal worktree tiers preserve first-seen worktree order, then use that worktree's MRU.
*/
export function orderRecentWorkspaceTabs(inputs: RecentWorkspaceTabOrderInputs): string[] {
const { rows, paneSources, now, lastVisitedAtByWorktreeId, focusedGroupTabRecency } = inputs
const worktreeOrder = new Map<string, number>()
for (const row of rows) {
if (!worktreeOrder.has(row.worktreeId)) {
worktreeOrder.set(row.worktreeId, worktreeOrder.size)
}
}
return rows
.map((row): RankedRow => {
const attention = resolveRecentWorkspaceTabAttention(row, paneSources, now)
@@ -153,6 +158,7 @@ export function orderRecentWorkspaceTabs(inputs: RecentWorkspaceTabOrderInputs):
attentionTimestamp: attention.attentionTimestamp,
visitedAt: lastVisitedAtByWorktreeId[row.worktreeId],
worktreeId: row.worktreeId,
worktreeOrder: worktreeOrder.get(row.worktreeId) ?? Number.MAX_SAFE_INTEGER,
focusOrdinal:
row.unifiedTabId === null
? NO_FOCUS_ORDINAL
@@ -21,7 +21,7 @@ import type { AgentMetadata } from './workspace-tab-agent-metadata'
*/
const AGENT_SNIPPET_RANK: PaletteDocumentRank = {
exactIntent: 1,
matchedDirectField: 0,
containerOnlyTokenCount: Number.MAX_SAFE_INTEGER,
wholeQuery: 3,
worstQuality: paletteMatchQualityRank(PALETTE_MATCH_QUALITIES.at(-1) as PaletteMatchQuality) + 1,
usesSupportingEvidence: 1,
@@ -99,13 +99,14 @@ function makeEntry({
describe('searchWorkspaceTabs lastActiveAt', () => {
it('is null when neither agent activity nor worktree activity is known', () => {
const [result] = searchWorkspaceTabs([makeEntry()], '')
const [result] = searchWorkspaceTabs([makeEntry({ createdAt: 4000 })], '')
expect(result.lastActiveAt).toBeNull()
})
it('falls back to worktree PTY activity for editor tabs with no agent metadata', () => {
const entry = makeEntry({
contentType: 'editor',
createdAt: 1000,
worktree: makeWorktree({ lastActivityAt: 5000 })
})
const [result] = searchWorkspaceTabs([entry], '')
@@ -148,9 +149,61 @@ describe('searchWorkspaceTabs lastActiveAt', () => {
const [result] = searchWorkspaceTabs([entry], '')
expect(result.lastActiveAt).toBe(6000)
})
it('uses a newer tab focus instead of stale agent activity', () => {
const entry = makeEntry({
id: 'tab-refocused',
createdAt: 1000,
agentLastActivityAt: 3000
})
entry.tab.lastFocusedAt = 8000
const [result] = searchWorkspaceTabs([entry], '')
expect(result.lastActiveAt).toBe(8000)
})
it('uses newer agent activity instead of an older tab focus', () => {
const entry = makeEntry({
id: 'tab-agent-active',
createdAt: 1000,
agentLastActivityAt: 8000
})
entry.tab.lastFocusedAt = 3000
const [result] = searchWorkspaceTabs([entry], '')
expect(result.lastActiveAt).toBe(8000)
})
})
describe('searchWorkspaceTabs ranking', () => {
it('ranks a multi-token direct-plus-container hit above a container-only whole-query hit', () => {
const directEntry = makeEntry({ id: 'direct-tab' })
const containerEntry = makeEntry({ id: 'container-tab' })
directEntry.document = buildPaletteTabDocument({
id: 'direct',
title: 'Auth notes',
secondaryTexts: [],
worktreeName: 'Aurora migration',
branch: 'main',
repoName: 'repo'
})
containerEntry.document = buildPaletteTabDocument({
id: 'container',
title: 'Unrelated notes',
secondaryTexts: [],
worktreeName: 'Auth Aurora',
branch: 'main',
repoName: 'repo'
})
const results = searchWorkspaceTabs([containerEntry, directEntry], 'auth aurora')
expect(results.map((result) => result.tabId)).toEqual(['direct-tab', 'container-tab'])
expect(results.map((result) => result.rank?.containerOnlyTokenCount)).toEqual([1, 2])
})
it('ranks direct tab title matches ahead of container-only worktree matches', () => {
const directEntry = makeEntry({ id: 'README-4360' })
const containerEntry = makeEntry({ id: 'unrelated-file' })
@@ -175,9 +228,9 @@ describe('searchWorkspaceTabs ranking', () => {
const results = searchWorkspaceTabs([containerEntry, directEntry], '4360')
expect(results).toHaveLength(2)
expect(results[0].tabId).toBe('README-4360')
expect(results[0].rank?.matchedDirectField).toBe(0)
expect(results[0].rank?.containerOnlyTokenCount).toBe(0)
expect(results[1].tabId).toBe('unrelated-file')
expect(results[1].rank?.matchedDirectField).toBe(1)
expect(results[1].rank?.containerOnlyTokenCount).toBe(1)
})
it('breaks tie between two container-matching tabs using lastActiveAt recency', () => {
@@ -87,15 +87,13 @@ function positionScore(entry: SearchableWorkspaceTab): number {
}
function resolveWorkspaceTabLastActiveAt(entry: SearchableWorkspaceTab): number | null {
// Agent activity wins when present; then explicit tab focus; otherwise fall back to worktree.
const candidate =
maxAgentActivityAt(entry.agentMetadata) ??
entry.tab.lastFocusedAt ??
(entry.worktree.lastActivityAt || null)
// Why: explicit tab activity outranks the worktree fallback; creation only clamps stale signals.
const tabLocalActivity =
Math.max(maxAgentActivityAt(entry.agentMetadata) ?? 0, entry.tab.lastFocusedAt ?? 0) || null
const candidate = tabLocalActivity || entry.worktree.lastActivityAt || null
if (candidate == null) {
return null
}
// Never report activity older than the tab itself (e.g. a stale worktree signal).
return Math.max(candidate, entry.tab.createdAt)
}
@@ -38,7 +38,7 @@ export function buildWorktreePaletteTaskUrlResult(args: {
qualityClass: 'exact-intent',
rank: {
exactIntent: 0,
matchedDirectField: 0,
containerOnlyTokenCount: 0,
wholeQuery: 0,
worstQuality: 0,
usesSupportingEvidence: 1,
+2
View File
@@ -147,6 +147,8 @@ const tabSchema = z.object({
color: z.string().nullable(),
sortOrder: z.number(),
createdAt: z.number(),
// Why: corrupt optional recency must not discard the whole persisted tab.
lastFocusedAt: z.number().finite().nonnegative().optional().catch(undefined),
isPreview: z.boolean().optional(),
isPinned: z.boolean().optional(),
// Why: persist the per-tab native-chat view mode so 'chat' survives reload /
@@ -0,0 +1,53 @@
import { describe, expect, it } from 'vitest'
import { parseWorkspaceSession } from './workspace-session-schema'
function sessionWithLastFocusedAt(lastFocusedAt: unknown): Record<string, unknown> {
return {
activeRepoId: null,
activeWorktreeId: null,
activeTabId: null,
tabsByWorktree: {},
terminalLayoutsByTabId: {},
unifiedTabs: {
wt: [
{
id: 'tab-1',
entityId: 'terminal-1',
groupId: 'group-1',
worktreeId: 'wt',
contentType: 'terminal',
label: 'Terminal',
customLabel: null,
color: null,
sortOrder: 0,
createdAt: 1,
lastFocusedAt
}
]
}
}
}
describe('workspace session unified-tab focus timestamp', () => {
it('preserves a finite nonnegative lastFocusedAt', () => {
const result = parseWorkspaceSession(sessionWithLastFocusedAt(1_700_000_000_123))
expect(result.ok).toBe(true)
if (result.ok) {
expect(result.value.unifiedTabs?.wt[0]?.lastFocusedAt).toBe(1_700_000_000_123)
}
})
it.each([-1, Number.NaN, Number.POSITIVE_INFINITY])(
'drops an invalid lastFocusedAt without dropping its tab (%s)',
(lastFocusedAt) => {
const result = parseWorkspaceSession(sessionWithLastFocusedAt(lastFocusedAt))
expect(result.ok).toBe(true)
if (result.ok) {
expect(result.value.unifiedTabs?.wt).toHaveLength(1)
expect(result.value.unifiedTabs?.wt[0]?.lastFocusedAt).toBeUndefined()
}
}
)
})