fix(github): align PR source and review head origin (#10677)

* fix(github): align PR source and review head origin

* fix(github): pin number-based work item open to the repo source preference

Open-by-number and details still ran the upstream-first multi-candidate PR
probe, so a fork and its upstream sharing a PR number opened different PRs
than the list and start-point paths did once #10677 pinned those to origin.

Thread repo.issueSourcePreference through dispatchWorkItem, getWorkItemDetails,
getRepoWorkItem, and getRepoWorkItemDetails. getWorkItemByOwnerRepo is left
alone: explicit owner/repo already pins identity. auto/upstream/undefined keep
the multi-candidate probe.

Co-authored-by: Orca <help@stably.ai>

* test(github): enforce origin preference in review head origin resolution

The explicit origin preference must short-circuit before any identity probe, so no remote queries should occur. Add validation to reject unexpected remotes and tighten the test assertion to verify no remote get-url calls happen at all.

* fix(github): enforce origin preference in issue open-by-number lookup

listWorkItems and getWorkItem must share preference so origin/upstream
toggles cannot disagree. Explicit origin preference now fail-closes when
origin identity is unresolved (no bare-lookup fallback), matching the
PR candidate resolution rule.

---------

Co-authored-by: Orca <help@stably.ai>
This commit is contained in:
Jinjing
2026-07-25 22:48:53 -07:00
committed by GitHub
co-authored by Orca
parent 8cb45318d7
commit 33bd676644
18 changed files with 654 additions and 69 deletions
+81 -11
View File
@@ -6123,6 +6123,45 @@ describe('OrcaRuntimeService', () => {
)
})
it('pins explicit origin preference on runtime open-by-number work item lookups', async () => {
const originRepo = {
id: TEST_REPO_ID,
path: TEST_REPO_PATH,
displayName: 'repo',
badgeColor: 'blue',
addedAt: 1,
issueSourcePreference: 'origin' as const
}
const runtime = new OrcaRuntimeService({
...store,
getRepos: () => [originRepo],
getRepo: (id: string) => (id === originRepo.id ? originRepo : undefined)
} as never)
const prRepo = { owner: 'acme', repo: 'orca' }
await runtime.getRepoWorkItem('id:repo-1', 42, 'pr')
await runtime.getRepoWorkItemDetails('id:repo-1', 42, 'pr')
await runtime.getRepoWorkItemByOwnerRepo('id:repo-1', prRepo, 42, 'pr')
expect(getGitHubWorkItemMock).toHaveBeenCalledWith(TEST_REPO_PATH, 42, 'pr', null, {}, 'origin')
expect(getGitHubWorkItemDetailsMock).toHaveBeenCalledWith(
TEST_REPO_PATH,
42,
'pr',
null,
{},
'origin'
)
// Why: explicit owner/repo already pins identity, so it stays preference-free.
expect(getGitHubWorkItemByOwnerRepoMock).toHaveBeenCalledWith(
TEST_REPO_PATH,
prRepo,
42,
'pr',
null
)
})
it('routes runtime GitHub PR details and actions through the selected WSL project runtime', async () => {
setPlatform('win32')
const runtimeStore = {
@@ -6220,7 +6259,8 @@ describe('OrcaRuntimeService', () => {
42,
'pr',
null,
localGitOptions
localGitOptions,
undefined
)
expect(getGitHubWorkItemByOwnerRepoMock).toHaveBeenCalledWith(
TEST_REPO_PATH,
@@ -6235,7 +6275,8 @@ describe('OrcaRuntimeService', () => {
42,
'pr',
null,
localGitOptions
localGitOptions,
undefined
)
expect(getGitHubPRChecksMock).toHaveBeenCalledWith(
TEST_REPO_PATH,
@@ -34533,10 +34574,20 @@ describe('OrcaRuntimeService', () => {
expect(updateSettings).not.toHaveBeenCalled()
})
it('routes runtime GitHub PR base git calls through the selected WSL project runtime', async () => {
it('threads explicit origin preference through runtime WSL PR base resolution', async () => {
setPlatform('win32')
const localRepo = {
id: TEST_REPO_ID,
path: TEST_REPO_PATH,
displayName: 'repo',
badgeColor: 'blue',
addedAt: 1,
issueSourcePreference: 'origin' as const
}
const runtimeStore = {
...store,
getRepos: () => [localRepo],
getRepo: (id: string) => (id === localRepo.id ? localRepo : undefined),
getProjects: () => [
{
id: 'project-1',
@@ -34564,8 +34615,18 @@ describe('OrcaRuntimeService', () => {
if (args[0] === 'config') {
return { stdout: 'origin\n', stderr: '' }
}
if (args[0] === 'remote' && args[1] === 'get-url') {
if (args[2] !== 'origin' && args[2] !== 'upstream') {
throw new Error(`unexpected remote: ${String(args[2])}`)
}
const url =
args[2] === 'origin'
? 'git@github.com:org/repo.git'
: 'git@github.com:org/upstream-repo.git'
return { stdout: `${url}\n`, stderr: '' }
}
if (args[0] === 'remote') {
return { stdout: 'origin\n', stderr: '' }
return { stdout: 'origin\nupstream\n', stderr: '' }
}
if (args[0] === 'fetch') {
return { stdout: '', stderr: '' }
@@ -34593,11 +34654,6 @@ describe('OrcaRuntimeService', () => {
headSha: 'pr-head-sha',
branchNameOverride: 'feature/add-feature'
})
expect(gitSpy).toHaveBeenCalledWith(['symbolic-ref', '--quiet', 'refs/remotes/origin/HEAD'], {
cwd: TEST_REPO_PATH,
timeout: 15_000,
wslDistro: 'Ubuntu'
})
expect(gitSpy).toHaveBeenCalledWith(
[
'fetch',
@@ -34610,6 +34666,12 @@ describe('OrcaRuntimeService', () => {
cwd: TEST_REPO_PATH,
wslDistro: 'Ubuntu'
})
// Why: the explicit origin preference must short-circuit before any
// identity probe, so no remote — not just upstream — gets a get-url.
expect(gitSpy).not.toHaveBeenCalledWith(
['remote', 'get-url', expect.anything()],
expect.anything()
)
} finally {
gitSpy.mockRestore()
}
@@ -34622,7 +34684,8 @@ describe('OrcaRuntimeService', () => {
displayName: 'repo',
badgeColor: 'blue',
addedAt: 1,
connectionId: 'ssh-1'
connectionId: 'ssh-1',
issueSourcePreference: 'origin' as const
}
const runtimeStore = {
...store,
@@ -34635,7 +34698,7 @@ describe('OrcaRuntimeService', () => {
return { stdout: `${ORIGIN_REMOTE_URL}\n`, stderr: '' }
}
if (args[0] === 'remote') {
return { stdout: 'origin\n', stderr: '' }
return { stdout: 'origin\nupstream\n', stderr: '' }
}
if (
args[0] === 'rev-parse' &&
@@ -34666,6 +34729,13 @@ describe('OrcaRuntimeService', () => {
branchNameOverride: 'contributor/fix'
})
expect(provider.fetchGitHubPullRequestHead).toHaveBeenCalledWith('/remote/repo', 'origin', 42)
expect(getPullRequestPushTargetMock).toHaveBeenCalledWith(
'/remote/repo',
42,
'ssh-1',
{},
'origin'
)
expect(provider.exec).not.toHaveBeenCalledWith(
expect.arrayContaining(['fetch']),
'/remote/repo'