Note
Grok responding on behalf of Julius.
Triage
Thanks @Absterrg0 for the clear write-up. I read this against main (9bd1d8009a, which contains the reported d60a71ef6). The report matches the code, and I didn't find another issue for this handoff landing in the wrong project.
What I found
- A hosted reference whose repository matches no local checkout is still served.
requireProjectinapps/server/src/pullRequest/PullRequestService.tsprefers a checkout of that repository, then takes the oldest non-Azure project on the host (projection_projectsisORDER BY created_at ASC, project_id ASC). That donor project is how a read borrows another checkout's credentials, and the test "routes a hosted reference to another repository through a project on that host" covers the read. Azure already requires a matching repository and is left out of this fallback. - The donor is then presented as the pull request's project. On that path the route keeps the donor's project id and
workspaceRootand only rewritesrepositoryandremote, anddetailandsummarycopy those intoprojectIdandworkspaceRoot. Since the match is a string compare of the storedowner/repo, a link toold-org/appresolves to the oldest project even when another project is the checkout of the renamedacme/app. GitHub follows the rename, so the read itself succeeds. - Resolve, Fix, and Checkout follow that identity. The thread-details row (
ThreadDetailsPrRow→usePullRequestHandoffs) opens the thread ondetail.projectIdand runsgit.preparePullRequestThreadwithcwdset todetail.workspaceRoot.PullRequestDetailPanelhas its own copy of the same handoff and uses the same values when opened from a thread. The success toast is the checkout toast, so a wrong project still looks successful. GitManager.preparePullRequestThreaddoesn't check that the cwd is a checkout of the pull request's repository. Worktree mode fetches the head throughmaterializePullRequestHeadBranch(ensureRemoteplus a fetch into that cwd), which is why the worktree shows up under the unrelated project. For comparison, a hostless reference naming another repository already fails with "The change request does not belong to the selected project."
Likely fix area
Some options, which could keep reads working through another checkout's credentials while not using that donor as the checkout target:
- Have
detailandsummaryidentify only a real checkout of the repository, or leave the checkout target empty when there isn't one. - Have both handoff paths refuse with a clear message when no project is a checkout of that repository.
- Add a guard in
preparePullRequestThreadthat refuses a cwd whose remote is a different repository. - Separately, rewrite a stored link to the canonical
owner/repothe host returns, so a renamed or transferred repository matches the checkout that already has the new remote.
Until then, re-linking under the current repository name, or checking the branch out in the right project manually, avoids it.
A maintainer will decide on the fix direction.