fix(new-workspace): prevent stale GitHub URL selection

This commit is contained in:
Neil
2026-08-23 14:34:39 -07:00
parent d1127b9939
commit d4eef1dab8
4 changed files with 311 additions and 0 deletions
@@ -639,6 +639,13 @@ export default function SmartWorkspaceNameField({
() => (sourceQueryWithinLimit ? parseGitHubIssueOrPRLink(debouncedQuery) : null),
[debouncedQuery, sourceQueryWithinLimit]
)
const githubUrlIntent = useMemo(
() =>
isSmartWorkspaceSourceQueryWithinLimit(value) && (mode === 'smart' || mode === 'github')
? parseGitHubIssueOrPRLink(value)
: null,
[mode, value]
)
const linearUrlIntent = useMemo(
() => parseBoundedSmartWorkspaceLinearIssueUrlIntent(value),
[value]
@@ -1270,6 +1277,7 @@ export default function SmartWorkspaceNameField({
value,
debouncedQuery
}),
githubUrlIntent,
gitlabAvailable: gitlabSourceAvailable,
gitlabItems: getVisibleHeldProviderResults({
items: gitlabItems,
@@ -1302,6 +1310,7 @@ export default function SmartWorkspaceNameField({
branchResultsSource,
debouncedQuery,
githubItems,
githubUrlIntent,
gitlabSourceAvailable,
gitlabItems,
jiraSource.accountChoices,
@@ -230,6 +230,100 @@ describe('Branch source results', () => {
).toEqual([{ id: 'default-1' }])
})
it('hides unrelated held results while a full GitHub URL resolves', () => {
const targetUrl = 'https://github.com/acme/widgets/issues/42'
const staleGitHubItem = {
id: 'issue-17',
type: 'issue' as const,
number: 17,
title: 'Wrong cached issue',
state: 'open' as const,
url: 'https://github.com/acme/widgets/issues/17',
labels: [],
updatedAt: '2026-08-01T00:00:00.000Z',
author: 'octocat',
repoId: 'repo-1'
}
expect(
buildSmartWorkspaceSourceRows({
mode: 'smart',
value: targetUrl,
githubUrlIntent: {
slug: { owner: 'acme', repo: 'widgets', host: 'github.com' },
type: 'issue',
number: 42
},
branches: [{ refName: 'origin/issues/17', localBranchName: 'issues/17' }],
githubItems: [
staleGitHubItem,
{
...staleGitHubItem,
id: 'same-number-other-repo',
number: 42,
url: 'https://github.com/acme/other-repo/issues/42'
},
{
...staleGitHubItem,
id: 'same-number-pull-request',
type: 'pr',
number: 42,
url: 'https://github.com/acme/widgets/pull/42'
}
],
gitlabItems: [],
linearIssues: [],
gitlabAvailable: false,
linearAvailable: false,
resultLimit: 12
})
).toEqual([{ kind: 'use-name', value: 'use-name', name: targetUrl }])
})
it('shows only the exact GitHub URL result after lookup settles', () => {
const targetUrl = 'https://github.com/acme/widgets/issues/42'
const exactItem = {
id: 'issue-42',
type: 'issue' as const,
number: 42,
title: 'Exact pasted issue',
state: 'open' as const,
url: targetUrl,
labels: [],
updatedAt: '2026-08-02T00:00:00.000Z',
author: 'octocat',
repoId: 'repo-1'
}
const staleItem = {
...exactItem,
id: 'issue-17',
number: 17,
url: `${targetUrl.slice(0, -2)}17`
}
expect(
buildSmartWorkspaceSourceRows({
mode: 'smart',
value: targetUrl,
githubUrlIntent: {
slug: { owner: 'ACME', repo: 'Widgets', host: 'GitHub.com' },
type: 'issue',
number: 42
},
branches: [],
githubItems: [staleItem, exactItem],
gitlabItems: [],
linearIssues: [],
gitlabAvailable: false,
linearAvailable: false,
resultLimit: 12
})
).toEqual([
{ kind: 'use-name', value: 'use-name', name: targetUrl },
{ kind: 'github', value: 'github-repo-1-issue-42', item: exactItem }
])
})
it('uses stable cmdk values for typed-text actions', () => {
expect(
buildSmartWorkspaceSourceRows({
@@ -5,6 +5,8 @@ import type { LinearIssue } from '../linear/issue-types'
import type { LinearCollectionResult } from '../linear/workspace-types'
import type { BaseRefSearchResult } from '../repo-types'
import { JIRA_ISSUE_KEY_PATTERN, parseJiraIssueUrl } from '../jira-issue-url'
import { parseGitHubIssueOrPRLink, type GitHubIssueOrPRLink } from '../github/links'
import { githubRepoIdentityKey } from '../github/repository-identity-key'
import {
isSmartWorkspaceLinearIssueIntentMatch,
parseBoundedSmartWorkspaceLinearIssueUrlIntent
@@ -63,6 +65,16 @@ function toJiraSourceRow(issue: JiraIssue): SmartWorkspaceSourceRow {
return { kind: 'jira', value: `jira-${issue.siteId ?? ''}-${issue.key}`, issue }
}
function isGitHubLinkIntentMatch(intent: GitHubIssueOrPRLink, item: GitHubWorkItem): boolean {
const itemLink = parseGitHubIssueOrPRLink(item.url)
return (
itemLink !== null &&
itemLink.type === intent.type &&
itemLink.number === intent.number &&
githubRepoIdentityKey(itemLink.slug) === githubRepoIdentityKey(intent.slug)
)
}
export function getBranchSearchRequest({
branchesEnabled,
disabled,
@@ -186,6 +198,7 @@ export function shouldHoldSourceResultsForQuery({
export function buildSmartWorkspaceSourceRows({
branches,
githubItems,
githubUrlIntent,
gitlabAvailable,
gitlabItems,
jiraIntent = false,
@@ -200,6 +213,7 @@ export function buildSmartWorkspaceSourceRows({
}: {
branches: BaseRefSearchResult[]
githubItems: GitHubWorkItem[]
githubUrlIntent?: GitHubIssueOrPRLink | null
gitlabAvailable: boolean
gitlabItems: GitLabWorkItem[]
jiraIntent?: boolean
@@ -220,6 +234,20 @@ export function buildSmartWorkspaceSourceRows({
return []
}
const trimmed = value.trim()
if (githubUrlIntent && (mode === 'smart' || mode === 'github')) {
const githubRows = githubItems
.filter((item) => isGitHubLinkIntentMatch(githubUrlIntent, item))
.map((item) => ({
kind: 'github' as const,
value: `github-${item.repoId}-${item.type}-${item.number}`,
item
}))
.slice(0, resultLimit)
// Why: the full URL is unambiguous, so unrelated held provider rows must never become selectable.
return trimmed && mode === 'smart'
? [{ kind: 'use-name' as const, value: 'use-name', name: trimmed }, ...githubRows]
: githubRows
}
const nextRows: SmartWorkspaceSourceRow[] = []
const resolvedLinearIssues = Array.isArray(linearIssues)
? linearIssues
@@ -0,0 +1,180 @@
import type { ElectronApplication, Page } from '@stablyai/playwright-test'
import type { GitHubWorkItem } from '../../src/shared/github/work-item-types'
import { test, expect } from './helpers/orca-app'
import { waitForActiveWorktree, waitForSessionReady } from './helpers/store'
const TARGET_URL = 'https://github.com/stablyai/orca/issues/4242'
const WRONG_TITLE = 'Wrong cached issue'
const TARGET_TITLE = 'Exact pasted issue'
const WRONG_ITEM: GitHubWorkItem = {
id: 'issue-17',
type: 'issue',
number: 17,
title: WRONG_TITLE,
state: 'open',
url: 'https://github.com/stablyai/orca/issues/17',
labels: [],
updatedAt: '2026-08-01T00:00:00.000Z',
author: 'e2e',
repoId: 'e2e-repo'
}
const TARGET_ITEM: GitHubWorkItem = {
...WRONG_ITEM,
id: 'issue-4242',
number: 4242,
title: TARGET_TITLE,
url: TARGET_URL,
updatedAt: '2026-08-02T00:00:00.000Z'
}
type TransitionFrame = {
value: string
wrongVisible: boolean
wrongSelected: boolean
targetVisible: boolean
targetSelected: boolean
}
function pasteChord(): string {
return process.platform === 'darwin' ? 'Meta+V' : 'Control+V'
}
async function installHeldGitHubLookup(
electronApp: ElectronApplication,
page: Page
): Promise<void> {
await electronApp.evaluate(({ ipcMain }, targetItem) => {
const fixture = globalThis as unknown as {
__githubUrlLookupStarted?: boolean
__releaseGitHubUrlLookup?: () => void
}
fixture.__githubUrlLookupStarted = false
ipcMain.removeHandler('gh:workItemByOwnerRepo')
ipcMain.handle('gh:workItemByOwnerRepo', () => {
fixture.__githubUrlLookupStarted = true
return new Promise((resolve) => {
fixture.__releaseGitHubUrlLookup = () => resolve(targetItem)
})
})
}, TARGET_ITEM)
await page.evaluate((wrongItem) => {
const store = window.__store
if (!store) {
throw new Error('window.__store is not available')
}
store.setState({
getCachedWorkItems: () => [wrongItem],
fetchWorkItems: async () => [wrongItem],
fetchWorkItemsAcrossRepos: async () => ({
items: [wrongItem],
failedCount: 0,
githubUnavailable: false
})
})
}, WRONG_ITEM)
}
async function releaseGitHubLookup(electronApp: ElectronApplication): Promise<void> {
await electronApp.evaluate(() => {
const fixture = globalThis as unknown as { __releaseGitHubUrlLookup?: () => void }
if (!fixture.__releaseGitHubUrlLookup) {
throw new Error('GitHub lookup is not held')
}
fixture.__releaseGitHubUrlLookup()
})
}
test('a pasted GitHub URL never selects a stale cached issue', async ({
electronApp,
orcaPage
}, testInfo) => {
await waitForSessionReady(orcaPage)
await waitForActiveWorktree(orcaPage)
await installHeldGitHubLookup(electronApp, orcaPage)
await orcaPage.getByRole('button', { name: 'New workspace', exact: true }).click()
const dialog = orcaPage.getByRole('dialog', { name: /Create (Workspace|Worktree)/i })
const input = dialog.locator('[data-workspace-name-input="true"]')
await expect(input).toBeVisible()
await input.click()
const wrongOption = orcaPage.getByRole('option', { name: `#17 ${WRONG_TITLE}`, exact: true })
const targetOption = orcaPage.getByRole('option', {
name: `#4242 ${TARGET_TITLE}`,
exact: true
})
await expect(wrongOption).toBeVisible()
await orcaPage.evaluate(
({ wrongTitle, targetTitle }) => {
const frames: TransitionFrame[] = []
const capture = (): void => {
const input = document.querySelector<HTMLInputElement>('[data-workspace-name-input="true"]')
const options = [...document.querySelectorAll<HTMLElement>('[role="option"]')]
const wrong = options.find((option) => option.textContent?.includes(wrongTitle))
const target = options.find((option) => option.textContent?.includes(targetTitle))
frames.push({
value: input?.value ?? '',
wrongVisible: Boolean(wrong && wrong.getClientRects().length > 0),
wrongSelected: wrong?.dataset.selected === 'true',
targetVisible: Boolean(target && target.getClientRects().length > 0),
targetSelected: target?.dataset.selected === 'true'
})
requestAnimationFrame(capture)
}
Reflect.set(window, '__githubUrlTransitionFrames', frames)
capture()
},
{ wrongTitle: WRONG_TITLE, targetTitle: TARGET_TITLE }
)
await orcaPage.evaluate((text) => window.api.ui.writeClipboardText(text), TARGET_URL)
await input.focus()
await orcaPage.keyboard.press(pasteChord())
await expect
.poll(() =>
electronApp.evaluate(() => {
const fixture = globalThis as unknown as { __githubUrlLookupStarted?: boolean }
return fixture.__githubUrlLookupStarted === true
})
)
.toBe(true)
await expect
.poll(() =>
orcaPage.evaluate((url) => {
const frames = Reflect.get(window, '__githubUrlTransitionFrames') as TransitionFrame[]
return frames.filter((frame) => frame.value === url).length
}, TARGET_URL)
)
.toBeGreaterThan(10)
await expect(wrongOption).toHaveCount(0)
await expect(targetOption).toHaveCount(0)
await releaseGitHubLookup(electronApp)
await expect(targetOption).toBeVisible()
await expect(targetOption).toHaveAttribute('data-selected', 'true')
await expect
.poll(() =>
orcaPage.evaluate(() => {
const frames = Reflect.get(window, '__githubUrlTransitionFrames') as TransitionFrame[]
return frames.some((frame) => frame.targetVisible && frame.targetSelected)
})
)
.toBe(true)
const frames = await orcaPage.evaluate(() => {
return Reflect.get(window, '__githubUrlTransitionFrames') as TransitionFrame[]
})
const pastedFrames = frames.filter((frame) => frame.value === TARGET_URL)
expect(frames.some((frame) => frame.wrongVisible)).toBe(true)
expect(pastedFrames.length).toBeGreaterThan(10)
expect(pastedFrames.every((frame) => !frame.wrongVisible && !frame.wrongSelected)).toBe(true)
expect(pastedFrames.some((frame) => frame.targetVisible && frame.targetSelected)).toBe(true)
await testInfo.attach('github-url-smart-input-fixed.png', {
body: await orcaPage.screenshot(),
contentType: 'image/png'
})
})