diff --git a/src/main/github/client-issue-source.test.ts b/src/main/github/client-issue-source.test.ts index 2b4a039b36a..7c64d5fd184 100644 --- a/src/main/github/client-issue-source.test.ts +++ b/src/main/github/client-issue-source.test.ts @@ -36,6 +36,10 @@ vi.mock('./gh-utils', async () => { execFileAsync: execFileAsyncMock, ghExecFileAsync: ghExecFileAsyncMock, getOwnerRepo: getOwnerRepoMock, + // Why: resolvePrWorkItemSource resolves the raw origin candidate via + // getOriginOwnerRepo; map it to the same mock these tests use for the + // origin PR repo. + getOriginOwnerRepo: getOwnerRepoMock, getIssueOwnerRepo: getIssueOwnerRepoMock, getOwnerRepoForRemote: getOwnerRepoForRemoteMock, resolveIssueSource: resolveIssueSourceMock, diff --git a/src/main/github/client-work-items.test.ts b/src/main/github/client-work-items.test.ts index e6ce6005c69..f39f2207295 100644 --- a/src/main/github/client-work-items.test.ts +++ b/src/main/github/client-work-items.test.ts @@ -50,6 +50,9 @@ vi.mock('./gh-utils', () => ({ ? {} : { cwd: context.repoPath, ...(context.wslDistro ? { wslDistro: context.wslDistro } : {}) }, getOwnerRepo: getOwnerRepoMock, + // Why: resolvePrWorkItemSource resolves the raw origin candidate via + // getOriginOwnerRepo; map it to the same mock these tests use for origin. + getOriginOwnerRepo: getOwnerRepoMock, getIssueOwnerRepo: getIssueOwnerRepoMock, getOwnerRepoForRemote: getOwnerRepoForRemoteMock, resolveIssueSource: resolveIssueSourceMock, diff --git a/src/main/github/client.ts b/src/main/github/client.ts index 5c8a1f4b514..e7b857b24fa 100644 --- a/src/main/github/client.ts +++ b/src/main/github/client.ts @@ -993,8 +993,12 @@ async function resolvePrWorkItemSource( connectionId?: string | null, localGitOptions: LocalGitExecOptions = {} ): Promise { + // Why: originCandidate must stay the raw `origin` remote (not upstream-first), + // so the renderer can compare it against upstream to decide whether to show the + // issue-source selector. getOwnerRepo is now upstream-first, so use the + // origin-specific resolver here. const [originCandidate, upstreamCandidate] = await Promise.all([ - getOwnerRepo(repoPath, connectionId, localGitOptions), + getOriginOwnerRepo(repoPath, connectionId, localGitOptions), getOwnerRepoForRemote(repoPath, 'upstream', connectionId, localGitOptions) ]) const source = @@ -1898,12 +1902,13 @@ export async function getWorkItem( return issue } } catch (err) { - // Why: the issue lookup now targets `upstream` while the PR lookup targets `origin`, - // so a transient upstream failure (5xx, rate limit, network flake) on issue #N would - // silently fall through to origin's PR #N — potentially a completely unrelated item. - // Only fall through when the issue genuinely doesn't exist (404); re-throw everything - // else so the outer catch returns null and the caller sees a real failure instead of - // a wrong item. classifyGhError centralizes the 404/"not found" pattern-matching. + // Why: issue and PR lookups both resolve upstream-first, so a transient + // upstream failure (5xx, rate limit, network flake) on issue #N would + // silently fall through to PR #N — a different item that happens to share + // the number. Only fall through when the issue genuinely doesn't exist + // (404); re-throw everything else so the outer catch returns null and the + // caller sees a real failure instead of a wrong item. classifyGhError + // centralizes the 404/"not found" pattern-matching. const stderr = err instanceof Error ? err.message : String(err) if (classifyGhError(stderr).type !== 'not_found') { throw err diff --git a/src/main/github/work-item-details.ts b/src/main/github/work-item-details.ts index 6029613fb3d..d4fa0c03b0e 100644 --- a/src/main/github/work-item-details.ts +++ b/src/main/github/work-item-details.ts @@ -801,9 +801,9 @@ async function getWorkItemParticipants( connectionId?: string | null, localGitOptions: LocalGitExecOptions = {} ): Promise { - // Why: issues in a fork live on the upstream remote, so participants must be - // resolved via getIssueOwnerRepo to stay consistent with getIssueBodyAndComments. - // PRs remain tied to origin via getOwnerRepo. + // Why: both issues and PRs in a fork live on the upstream remote, so + // participants are resolved via getIssueOwnerRepo/getOwnerRepo, which both + // resolve upstream-first, staying consistent with getIssueBodyAndComments. const ownerRepo = item.type === 'issue' ? await getIssueOwnerRepo(repoPath, connectionId, ...localGitOptionArgs(localGitOptions))