Skip to content

fix(server): follow selected pull request remote - #79

Merged
incognitojam merged 3 commits into
mainfrom
t3code/fix-pull-request-remote-selection
Aug 11, 2026
Merged

fix(server): follow selected pull request remote#79
incognitojam merged 3 commits into
mainfrom
t3code/fix-pull-request-remote-selection

Conversation

@incognitojam

@incognitojam incognitojam commented Aug 11, 2026

Copy link
Copy Markdown
Collaborator

T3 Code preferred upstream when identifying repositories with multiple remotes, which could show pull requests from the wrong repository and associate a worktree with a same-named upstream branch.

Resolve repository identity from the current branch tracked remote first, then the repository selected by gh repo set-default, before applying the existing fallback. Support both gh-resolved = base and explicit owner/repo selections, and key the identity cache by tracked remote so branch changes take effect immediately. Current-worktree PR lookup remains scoped to GitHub CLI’s base repository and uses the existing head repository/owner matching, preserving fork-to-parent PR detection without accepting a same-named PR from another fork.

Verification:

  • vp test run apps/server/src/project/RepositoryIdentityResolver.test.ts apps/server/src/git/GitManager.test.ts — 89 tests passed
  • vp run --filter t3 typecheck
  • Targeted lint, formatting, and diff checks
  • Independent Oracle review informed the corrected head-vs-base repository handling

Model: GPT-5.6-sol; harness: Codex.

@github-actions github-actions Bot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:M labels Aug 11, 2026
@github-actions github-actions Bot added size:XXL and removed size:M labels Aug 11, 2026
@incognitojam
incognitojam force-pushed the t3code/fix-pull-request-remote-selection branch from e881d73 to 23c6e7d Compare August 11, 2026 13:09
@github-actions github-actions Bot added size:M and removed size:XXL labels Aug 11, 2026
@incognitojam
incognitojam force-pushed the t3code/fix-pull-request-remote-selection branch from 23c6e7d to c74aaab Compare August 11, 2026 14:59
@github-actions github-actions Bot added size:L and removed size:M labels Aug 11, 2026
@incognitojam
incognitojam added this pull request to the merge queue Aug 11, 2026
Merged via the queue into main with commit e7d1a38 Aug 11, 2026
12 checks passed
@incognitojam
incognitojam deleted the t3code/fix-pull-request-remote-selection branch August 11, 2026 15:27
incognitojam added a commit that referenced this pull request Aug 11, 2026
T3 Code preferred `upstream` when identifying repositories with multiple
remotes, which could show pull requests from the wrong repository and
associate a worktree with a same-named upstream branch.

Resolve repository identity from the current branch tracked remote
first, then the repository selected by `gh repo set-default`, before
applying the existing fallback. Support both `gh-resolved = base` and
explicit `owner/repo` selections, and key the identity cache by tracked
remote so branch changes take effect immediately. Current-worktree PR
lookup remains scoped to GitHub CLI’s base repository and uses the
existing head repository/owner matching, preserving fork-to-parent PR
detection without accepting a same-named PR from another fork.

Verification:
- `vp test run
apps/server/src/project/RepositoryIdentityResolver.test.ts
apps/server/src/git/GitManager.test.ts` — 89 tests passed
- `vp run --filter t3 typecheck`
- Targeted lint, formatting, and diff checks
- Independent Oracle review informed the corrected head-vs-base
repository handling

Model: GPT-5.6-sol; harness: Codex.
yngatech-nightly Bot pushed a commit that referenced this pull request Aug 11, 2026
T3 Code preferred `upstream` when identifying repositories with multiple
remotes, which could show pull requests from the wrong repository and
associate a worktree with a same-named upstream branch.

Resolve repository identity from the current branch tracked remote
first, then the repository selected by `gh repo set-default`, before
applying the existing fallback. Support both `gh-resolved = base` and
explicit `owner/repo` selections, and key the identity cache by tracked
remote so branch changes take effect immediately. Current-worktree PR
lookup remains scoped to GitHub CLI’s base repository and uses the
existing head repository/owner matching, preserving fork-to-parent PR
detection without accepting a same-named PR from another fork.

Verification:
- `vp test run
apps/server/src/project/RepositoryIdentityResolver.test.ts
apps/server/src/git/GitManager.test.ts` — 89 tests passed
- `vp run --filter t3 typecheck`
- Targeted lint, formatting, and diff checks
- Independent Oracle review informed the corrected head-vs-base
repository handling

Model: GPT-5.6-sol; harness: Codex.
yngatech-nightly Bot pushed a commit that referenced this pull request Aug 12, 2026
T3 Code preferred `upstream` when identifying repositories with multiple
remotes, which could show pull requests from the wrong repository and
associate a worktree with a same-named upstream branch.

Resolve repository identity from the current branch tracked remote
first, then the repository selected by `gh repo set-default`, before
applying the existing fallback. Support both `gh-resolved = base` and
explicit `owner/repo` selections, and key the identity cache by tracked
remote so branch changes take effect immediately. Current-worktree PR
lookup remains scoped to GitHub CLI’s base repository and uses the
existing head repository/owner matching, preserving fork-to-parent PR
detection without accepting a same-named PR from another fork.

Verification:
- `vp test run
apps/server/src/project/RepositoryIdentityResolver.test.ts
apps/server/src/git/GitManager.test.ts` — 89 tests passed
- `vp run --filter t3 typecheck`
- Targeted lint, formatting, and diff checks
- Independent Oracle review informed the corrected head-vs-base
repository handling

Model: GPT-5.6-sol; harness: Codex.
yngatech-nightly Bot pushed a commit that referenced this pull request Aug 12, 2026
T3 Code preferred `upstream` when identifying repositories with multiple
remotes, which could show pull requests from the wrong repository and
associate a worktree with a same-named upstream branch.

Resolve repository identity from the current branch tracked remote
first, then the repository selected by `gh repo set-default`, before
applying the existing fallback. Support both `gh-resolved = base` and
explicit `owner/repo` selections, and key the identity cache by tracked
remote so branch changes take effect immediately. Current-worktree PR
lookup remains scoped to GitHub CLI’s base repository and uses the
existing head repository/owner matching, preserving fork-to-parent PR
detection without accepting a same-named PR from another fork.

Verification:
- `vp test run
apps/server/src/project/RepositoryIdentityResolver.test.ts
apps/server/src/git/GitManager.test.ts` — 89 tests passed
- `vp run --filter t3 typecheck`
- Targeted lint, formatting, and diff checks
- Independent Oracle review informed the corrected head-vs-base
repository handling

Model: GPT-5.6-sol; harness: Codex.
yngatech-nightly Bot pushed a commit that referenced this pull request Aug 12, 2026
T3 Code preferred `upstream` when identifying repositories with multiple
remotes, which could show pull requests from the wrong repository and
associate a worktree with a same-named upstream branch.

Resolve repository identity from the current branch tracked remote
first, then the repository selected by `gh repo set-default`, before
applying the existing fallback. Support both `gh-resolved = base` and
explicit `owner/repo` selections, and key the identity cache by tracked
remote so branch changes take effect immediately. Current-worktree PR
lookup remains scoped to GitHub CLI’s base repository and uses the
existing head repository/owner matching, preserving fork-to-parent PR
detection without accepting a same-named PR from another fork.

Verification:
- `vp test run
apps/server/src/project/RepositoryIdentityResolver.test.ts
apps/server/src/git/GitManager.test.ts` — 89 tests passed
- `vp run --filter t3 typecheck`
- Targeted lint, formatting, and diff checks
- Independent Oracle review informed the corrected head-vs-base
repository handling

Model: GPT-5.6-sol; harness: Codex.
yngatech-nightly Bot pushed a commit that referenced this pull request Aug 13, 2026
T3 Code preferred `upstream` when identifying repositories with multiple
remotes, which could show pull requests from the wrong repository and
associate a worktree with a same-named upstream branch.

Resolve repository identity from the current branch tracked remote
first, then the repository selected by `gh repo set-default`, before
applying the existing fallback. Support both `gh-resolved = base` and
explicit `owner/repo` selections, and key the identity cache by tracked
remote so branch changes take effect immediately. Current-worktree PR
lookup remains scoped to GitHub CLI’s base repository and uses the
existing head repository/owner matching, preserving fork-to-parent PR
detection without accepting a same-named PR from another fork.

Verification:
- `vp test run
apps/server/src/project/RepositoryIdentityResolver.test.ts
apps/server/src/git/GitManager.test.ts` — 89 tests passed
- `vp run --filter t3 typecheck`
- Targeted lint, formatting, and diff checks
- Independent Oracle review informed the corrected head-vs-base
repository handling

Model: GPT-5.6-sol; harness: Codex.
yngatech-nightly Bot pushed a commit that referenced this pull request Aug 13, 2026
T3 Code preferred `upstream` when identifying repositories with multiple
remotes, which could show pull requests from the wrong repository and
associate a worktree with a same-named upstream branch.

Resolve repository identity from the current branch tracked remote
first, then the repository selected by `gh repo set-default`, before
applying the existing fallback. Support both `gh-resolved = base` and
explicit `owner/repo` selections, and key the identity cache by tracked
remote so branch changes take effect immediately. Current-worktree PR
lookup remains scoped to GitHub CLI’s base repository and uses the
existing head repository/owner matching, preserving fork-to-parent PR
detection without accepting a same-named PR from another fork.

Verification:
- `vp test run
apps/server/src/project/RepositoryIdentityResolver.test.ts
apps/server/src/git/GitManager.test.ts` — 89 tests passed
- `vp run --filter t3 typecheck`
- Targeted lint, formatting, and diff checks
- Independent Oracle review informed the corrected head-vs-base
repository handling

Model: GPT-5.6-sol; harness: Codex.
yngatech-nightly Bot pushed a commit that referenced this pull request Aug 13, 2026
T3 Code preferred `upstream` when identifying repositories with multiple
remotes, which could show pull requests from the wrong repository and
associate a worktree with a same-named upstream branch.

Resolve repository identity from the current branch tracked remote
first, then the repository selected by `gh repo set-default`, before
applying the existing fallback. Support both `gh-resolved = base` and
explicit `owner/repo` selections, and key the identity cache by tracked
remote so branch changes take effect immediately. Current-worktree PR
lookup remains scoped to GitHub CLI’s base repository and uses the
existing head repository/owner matching, preserving fork-to-parent PR
detection without accepting a same-named PR from another fork.

Verification:
- `vp test run
apps/server/src/project/RepositoryIdentityResolver.test.ts
apps/server/src/git/GitManager.test.ts` — 89 tests passed
- `vp run --filter t3 typecheck`
- Targeted lint, formatting, and diff checks
- Independent Oracle review informed the corrected head-vs-base
repository handling

Model: GPT-5.6-sol; harness: Codex.
incognitojam added a commit that referenced this pull request Aug 13, 2026
> [!NOTE]
> Cloning a GitHub fork now wires up the parent as `upstream` and pins
which repository `gh` targets, with the choice offered during the clone
and editable afterwards.

Cloning a fork left the parent repository unreachable — no `upstream`
remote — and, worse, `gh` resolves a two-remote checkout to the fork's
parent, so a pull request created from the workspace could land on
someone else's project without ever saying so.

A fork clone now adds `upstream`, fetches it the way `gh repo clone`
leaves it, and pins a default repository by writing the same
`remote.<name>.gh-resolved` config `gh repo set-default` uses, so T3
Code and the GitHub CLI always agree. Because two remotes mean two
possible targets, the clone flow asks which one; the cloned fork leads,
so the pin agrees with the remote a branch on the fork tracks, and the
parent stays one keystroke away for contributing upstream.

### Choosing during the clone

| Before | After |
| --- | --- |
| ![Lookup goes straight to the destination
step](https://raw.githubusercontent.com/yngatech/t3code/pr-assets/configure-fork-upstream-remote/clone-before.png?v=064d1d3f9)
| ![A step asking which repository is the
default](https://raw.githubusercontent.com/yngatech/t3code/pr-assets/configure-fork-upstream-remote/clone-after-step.png?v=064d1d3f9)
|

The destination step then states the relationship instead of repeating
the repository name as a URL:

![Destination step showing "forked from
CircuitLord/BigWalkVRInstaller"](https://raw.githubusercontent.com/yngatech/t3code/pr-assets/configure-fork-upstream-remote/clone-after-destination.png?v=064d1d3f9)

### Changing it later

| Before | After |
| --- | --- |
| ![Checkout settings without a default repository
row](https://raw.githubusercontent.com/yngatech/t3code/pr-assets/configure-fork-upstream-remote/settings-before.png?v=064d1d3f9)
| ![Checkout settings with a Default repository
row](https://raw.githubusercontent.com/yngatech/t3code/pr-assets/configure-fork-upstream-remote/settings-after.png?v=064d1d3f9)
|

Mobile gets the same choice at clone time, plus a **Default repository**
row in the thread git sheet so the phone is not a one-way door:

<img
src="https://raw.githubusercontent.com/yngatech/t3code/pr-assets/configure-fork-upstream-remote/mobile-after.png?v=064d1d3f9"
width="320" alt="The fork chooser on iOS with owner avatars">

### Notes

- `gh-resolved` is not always `base`: with a single remote, `gh repo
set-default` writes an `owner/repo`. That value is parsed and shown
rather than silently overwritten, and the parse is now shared with
`RepositoryIdentityResolver` so the two cannot drift.
- The pin is written with `--replace-all`, since `gh` *adds* resolutions
and a plain write refuses to overwrite a multi-value key (exit 5).
- Only forks send `provider`/`repository` on clone, so every other clone
costs no extra `gh repo view` and behaves exactly as before against an
older server.
- Identity still follows the branch's tracked remote first, per #79; the
pin only decides when a branch has no upstream yet.

### Verification

- `vp test run apps/server/src/sourceControl apps/server/src/project
packages/shared/src/git.test.ts packages/client-runtime` — 797 tests
passed, including new cases for the `owner/repo` pin, the unset path,
dotted remote names, and a fetch failure leaving the clone intact
- Targeted typecheck, lint, and formatting across server, contracts,
shared, client-runtime, web, and mobile
- Web: clone flow and Settings driven by hand and captured above
- iOS Simulator: fork chooser verified on device; the git sheet row is
typechecked but unexercised, since it needs a thread with real git state
- Two independent agent reviews; findings folded in

---
Written by an agent (Claude Code, claude-opus-5).
yngatech-nightly Bot pushed a commit that referenced this pull request Aug 13, 2026
T3 Code preferred `upstream` when identifying repositories with multiple
remotes, which could show pull requests from the wrong repository and
associate a worktree with a same-named upstream branch.

Resolve repository identity from the current branch tracked remote
first, then the repository selected by `gh repo set-default`, before
applying the existing fallback. Support both `gh-resolved = base` and
explicit `owner/repo` selections, and key the identity cache by tracked
remote so branch changes take effect immediately. Current-worktree PR
lookup remains scoped to GitHub CLI’s base repository and uses the
existing head repository/owner matching, preserving fork-to-parent PR
detection without accepting a same-named PR from another fork.

Verification:
- `vp test run
apps/server/src/project/RepositoryIdentityResolver.test.ts
apps/server/src/git/GitManager.test.ts` — 89 tests passed
- `vp run --filter t3 typecheck`
- Targeted lint, formatting, and diff checks
- Independent Oracle review informed the corrected head-vs-base
repository handling

Model: GPT-5.6-sol; harness: Codex.
yngatech-nightly Bot pushed a commit that referenced this pull request Aug 13, 2026
> [!NOTE]
> Cloning a GitHub fork now wires up the parent as `upstream` and pins
which repository `gh` targets, with the choice offered during the clone
and editable afterwards.

Cloning a fork left the parent repository unreachable — no `upstream`
remote — and, worse, `gh` resolves a two-remote checkout to the fork's
parent, so a pull request created from the workspace could land on
someone else's project without ever saying so.

A fork clone now adds `upstream`, fetches it the way `gh repo clone`
leaves it, and pins a default repository by writing the same
`remote.<name>.gh-resolved` config `gh repo set-default` uses, so T3
Code and the GitHub CLI always agree. Because two remotes mean two
possible targets, the clone flow asks which one; the cloned fork leads,
so the pin agrees with the remote a branch on the fork tracks, and the
parent stays one keystroke away for contributing upstream.

### Choosing during the clone

| Before | After |
| --- | --- |
| ![Lookup goes straight to the destination
step](https://raw.githubusercontent.com/yngatech/t3code/pr-assets/configure-fork-upstream-remote/clone-before.png?v=064d1d3f9)
| ![A step asking which repository is the
default](https://raw.githubusercontent.com/yngatech/t3code/pr-assets/configure-fork-upstream-remote/clone-after-step.png?v=064d1d3f9)
|

The destination step then states the relationship instead of repeating
the repository name as a URL:

![Destination step showing "forked from
CircuitLord/BigWalkVRInstaller"](https://raw.githubusercontent.com/yngatech/t3code/pr-assets/configure-fork-upstream-remote/clone-after-destination.png?v=064d1d3f9)

### Changing it later

| Before | After |
| --- | --- |
| ![Checkout settings without a default repository
row](https://raw.githubusercontent.com/yngatech/t3code/pr-assets/configure-fork-upstream-remote/settings-before.png?v=064d1d3f9)
| ![Checkout settings with a Default repository
row](https://raw.githubusercontent.com/yngatech/t3code/pr-assets/configure-fork-upstream-remote/settings-after.png?v=064d1d3f9)
|

Mobile gets the same choice at clone time, plus a **Default repository**
row in the thread git sheet so the phone is not a one-way door:

<img
src="https://raw.githubusercontent.com/yngatech/t3code/pr-assets/configure-fork-upstream-remote/mobile-after.png?v=064d1d3f9"
width="320" alt="The fork chooser on iOS with owner avatars">

### Notes

- `gh-resolved` is not always `base`: with a single remote, `gh repo
set-default` writes an `owner/repo`. That value is parsed and shown
rather than silently overwritten, and the parse is now shared with
`RepositoryIdentityResolver` so the two cannot drift.
- The pin is written with `--replace-all`, since `gh` *adds* resolutions
and a plain write refuses to overwrite a multi-value key (exit 5).
- Only forks send `provider`/`repository` on clone, so every other clone
costs no extra `gh repo view` and behaves exactly as before against an
older server.
- Identity still follows the branch's tracked remote first, per #79; the
pin only decides when a branch has no upstream yet.

### Verification

- `vp test run apps/server/src/sourceControl apps/server/src/project
packages/shared/src/git.test.ts packages/client-runtime` — 797 tests
passed, including new cases for the `owner/repo` pin, the unset path,
dotted remote names, and a fetch failure leaving the clone intact
- Targeted typecheck, lint, and formatting across server, contracts,
shared, client-runtime, web, and mobile
- Web: clone flow and Settings driven by hand and captured above
- iOS Simulator: fork chooser verified on device; the git sheet row is
typechecked but unexercised, since it needs a thread with real git state
- Two independent agent reviews; findings folded in

---
Written by an agent (Claude Code, claude-opus-5).
yngatech-nightly Bot pushed a commit that referenced this pull request Aug 14, 2026
T3 Code preferred `upstream` when identifying repositories with multiple
remotes, which could show pull requests from the wrong repository and
associate a worktree with a same-named upstream branch.

Resolve repository identity from the current branch tracked remote
first, then the repository selected by `gh repo set-default`, before
applying the existing fallback. Support both `gh-resolved = base` and
explicit `owner/repo` selections, and key the identity cache by tracked
remote so branch changes take effect immediately. Current-worktree PR
lookup remains scoped to GitHub CLI’s base repository and uses the
existing head repository/owner matching, preserving fork-to-parent PR
detection without accepting a same-named PR from another fork.

Verification:
- `vp test run
apps/server/src/project/RepositoryIdentityResolver.test.ts
apps/server/src/git/GitManager.test.ts` — 89 tests passed
- `vp run --filter t3 typecheck`
- Targeted lint, formatting, and diff checks
- Independent Oracle review informed the corrected head-vs-base
repository handling

Model: GPT-5.6-sol; harness: Codex.
yngatech-nightly Bot pushed a commit that referenced this pull request Aug 14, 2026
> [!NOTE]
> Cloning a GitHub fork now wires up the parent as `upstream` and pins
which repository `gh` targets, with the choice offered during the clone
and editable afterwards.

Cloning a fork left the parent repository unreachable — no `upstream`
remote — and, worse, `gh` resolves a two-remote checkout to the fork's
parent, so a pull request created from the workspace could land on
someone else's project without ever saying so.

A fork clone now adds `upstream`, fetches it the way `gh repo clone`
leaves it, and pins a default repository by writing the same
`remote.<name>.gh-resolved` config `gh repo set-default` uses, so T3
Code and the GitHub CLI always agree. Because two remotes mean two
possible targets, the clone flow asks which one; the cloned fork leads,
so the pin agrees with the remote a branch on the fork tracks, and the
parent stays one keystroke away for contributing upstream.

| Before | After |
| --- | --- |
| ![Lookup goes straight to the destination
step](https://raw.githubusercontent.com/yngatech/t3code/pr-assets/configure-fork-upstream-remote/clone-before.png?v=064d1d3f9)
| ![A step asking which repository is the
default](https://raw.githubusercontent.com/yngatech/t3code/pr-assets/configure-fork-upstream-remote/clone-after-step.png?v=064d1d3f9)
|

The destination step then states the relationship instead of repeating
the repository name as a URL:

![Destination step showing "forked from
CircuitLord/BigWalkVRInstaller"](https://raw.githubusercontent.com/yngatech/t3code/pr-assets/configure-fork-upstream-remote/clone-after-destination.png?v=064d1d3f9)

| Before | After |
| --- | --- |
| ![Checkout settings without a default repository
row](https://raw.githubusercontent.com/yngatech/t3code/pr-assets/configure-fork-upstream-remote/settings-before.png?v=064d1d3f9)
| ![Checkout settings with a Default repository
row](https://raw.githubusercontent.com/yngatech/t3code/pr-assets/configure-fork-upstream-remote/settings-after.png?v=064d1d3f9)
|

Mobile gets the same choice at clone time, plus a **Default repository**
row in the thread git sheet so the phone is not a one-way door:

<img
src="https://raw.githubusercontent.com/yngatech/t3code/pr-assets/configure-fork-upstream-remote/mobile-after.png?v=064d1d3f9"
width="320" alt="The fork chooser on iOS with owner avatars">

- `gh-resolved` is not always `base`: with a single remote, `gh repo
set-default` writes an `owner/repo`. That value is parsed and shown
rather than silently overwritten, and the parse is now shared with
`RepositoryIdentityResolver` so the two cannot drift.
- The pin is written with `--replace-all`, since `gh` *adds* resolutions
and a plain write refuses to overwrite a multi-value key (exit 5).
- Only forks send `provider`/`repository` on clone, so every other clone
costs no extra `gh repo view` and behaves exactly as before against an
older server.
- Identity still follows the branch's tracked remote first, per #79; the
pin only decides when a branch has no upstream yet.

- `vp test run apps/server/src/sourceControl apps/server/src/project
packages/shared/src/git.test.ts packages/client-runtime` — 797 tests
passed, including new cases for the `owner/repo` pin, the unset path,
dotted remote names, and a fetch failure leaving the clone intact
- Targeted typecheck, lint, and formatting across server, contracts,
shared, client-runtime, web, and mobile
- Web: clone flow and Settings driven by hand and captured above
- iOS Simulator: fork chooser verified on device; the git sheet row is
typechecked but unexercised, since it needs a thread with real git state
- Two independent agent reviews; findings folded in

---
Written by an agent (Claude Code, claude-opus-5).
yngatech-nightly Bot pushed a commit that referenced this pull request Aug 14, 2026
T3 Code preferred `upstream` when identifying repositories with multiple
remotes, which could show pull requests from the wrong repository and
associate a worktree with a same-named upstream branch.

Resolve repository identity from the current branch tracked remote
first, then the repository selected by `gh repo set-default`, before
applying the existing fallback. Support both `gh-resolved = base` and
explicit `owner/repo` selections, and key the identity cache by tracked
remote so branch changes take effect immediately. Current-worktree PR
lookup remains scoped to GitHub CLI’s base repository and uses the
existing head repository/owner matching, preserving fork-to-parent PR
detection without accepting a same-named PR from another fork.

Verification:
- `vp test run
apps/server/src/project/RepositoryIdentityResolver.test.ts
apps/server/src/git/GitManager.test.ts` — 89 tests passed
- `vp run --filter t3 typecheck`
- Targeted lint, formatting, and diff checks
- Independent Oracle review informed the corrected head-vs-base
repository handling

Model: GPT-5.6-sol; harness: Codex.
yngatech-nightly Bot pushed a commit that referenced this pull request Aug 14, 2026
> [!NOTE]
> Cloning a GitHub fork now wires up the parent as `upstream` and pins
which repository `gh` targets, with the choice offered during the clone
and editable afterwards.

Cloning a fork left the parent repository unreachable — no `upstream`
remote — and, worse, `gh` resolves a two-remote checkout to the fork's
parent, so a pull request created from the workspace could land on
someone else's project without ever saying so.

A fork clone now adds `upstream`, fetches it the way `gh repo clone`
leaves it, and pins a default repository by writing the same
`remote.<name>.gh-resolved` config `gh repo set-default` uses, so T3
Code and the GitHub CLI always agree. Because two remotes mean two
possible targets, the clone flow asks which one; the cloned fork leads,
so the pin agrees with the remote a branch on the fork tracks, and the
parent stays one keystroke away for contributing upstream.

| Before | After |
| --- | --- |
| ![Lookup goes straight to the destination
step](https://raw.githubusercontent.com/yngatech/t3code/pr-assets/configure-fork-upstream-remote/clone-before.png?v=064d1d3f9)
| ![A step asking which repository is the
default](https://raw.githubusercontent.com/yngatech/t3code/pr-assets/configure-fork-upstream-remote/clone-after-step.png?v=064d1d3f9)
|

The destination step then states the relationship instead of repeating
the repository name as a URL:

![Destination step showing "forked from
CircuitLord/BigWalkVRInstaller"](https://raw.githubusercontent.com/yngatech/t3code/pr-assets/configure-fork-upstream-remote/clone-after-destination.png?v=064d1d3f9)

| Before | After |
| --- | --- |
| ![Checkout settings without a default repository
row](https://raw.githubusercontent.com/yngatech/t3code/pr-assets/configure-fork-upstream-remote/settings-before.png?v=064d1d3f9)
| ![Checkout settings with a Default repository
row](https://raw.githubusercontent.com/yngatech/t3code/pr-assets/configure-fork-upstream-remote/settings-after.png?v=064d1d3f9)
|

Mobile gets the same choice at clone time, plus a **Default repository**
row in the thread git sheet so the phone is not a one-way door:

<img
src="https://raw.githubusercontent.com/yngatech/t3code/pr-assets/configure-fork-upstream-remote/mobile-after.png?v=064d1d3f9"
width="320" alt="The fork chooser on iOS with owner avatars">

- `gh-resolved` is not always `base`: with a single remote, `gh repo
set-default` writes an `owner/repo`. That value is parsed and shown
rather than silently overwritten, and the parse is now shared with
`RepositoryIdentityResolver` so the two cannot drift.
- The pin is written with `--replace-all`, since `gh` *adds* resolutions
and a plain write refuses to overwrite a multi-value key (exit 5).
- Only forks send `provider`/`repository` on clone, so every other clone
costs no extra `gh repo view` and behaves exactly as before against an
older server.
- Identity still follows the branch's tracked remote first, per #79; the
pin only decides when a branch has no upstream yet.

- `vp test run apps/server/src/sourceControl apps/server/src/project
packages/shared/src/git.test.ts packages/client-runtime` — 797 tests
passed, including new cases for the `owner/repo` pin, the unset path,
dotted remote names, and a fetch failure leaving the clone intact
- Targeted typecheck, lint, and formatting across server, contracts,
shared, client-runtime, web, and mobile
- Web: clone flow and Settings driven by hand and captured above
- iOS Simulator: fork chooser verified on device; the git sheet row is
typechecked but unexercised, since it needs a thread with real git state
- Two independent agent reviews; findings folded in

---
Written by an agent (Claude Code, claude-opus-5).
incognitojam added a commit that referenced this pull request Aug 15, 2026
T3 Code preferred `upstream` when identifying repositories with multiple
remotes, which could show pull requests from the wrong repository and
associate a worktree with a same-named upstream branch.

Resolve repository identity from the current branch tracked remote
first, then the repository selected by `gh repo set-default`, before
applying the existing fallback. Support both `gh-resolved = base` and
explicit `owner/repo` selections, and key the identity cache by tracked
remote so branch changes take effect immediately. Current-worktree PR
lookup remains scoped to GitHub CLI’s base repository and uses the
existing head repository/owner matching, preserving fork-to-parent PR
detection without accepting a same-named PR from another fork.

Verification:
- `vp test run
apps/server/src/project/RepositoryIdentityResolver.test.ts
apps/server/src/git/GitManager.test.ts` — 89 tests passed
- `vp run --filter t3 typecheck`
- Targeted lint, formatting, and diff checks
- Independent Oracle review informed the corrected head-vs-base
repository handling

Model: GPT-5.6-sol; harness: Codex.
incognitojam added a commit that referenced this pull request Aug 15, 2026
> [!NOTE]
> Cloning a GitHub fork now wires up the parent as `upstream` and pins
which repository `gh` targets, with the choice offered during the clone
and editable afterwards.

Cloning a fork left the parent repository unreachable — no `upstream`
remote — and, worse, `gh` resolves a two-remote checkout to the fork's
parent, so a pull request created from the workspace could land on
someone else's project without ever saying so.

A fork clone now adds `upstream`, fetches it the way `gh repo clone`
leaves it, and pins a default repository by writing the same
`remote.<name>.gh-resolved` config `gh repo set-default` uses, so T3
Code and the GitHub CLI always agree. Because two remotes mean two
possible targets, the clone flow asks which one; the cloned fork leads,
so the pin agrees with the remote a branch on the fork tracks, and the
parent stays one keystroke away for contributing upstream.

| Before | After |
| --- | --- |
| ![Lookup goes straight to the destination
step](https://raw.githubusercontent.com/yngatech/t3code/pr-assets/configure-fork-upstream-remote/clone-before.png?v=064d1d3f9)
| ![A step asking which repository is the
default](https://raw.githubusercontent.com/yngatech/t3code/pr-assets/configure-fork-upstream-remote/clone-after-step.png?v=064d1d3f9)
|

The destination step then states the relationship instead of repeating
the repository name as a URL:

![Destination step showing "forked from
CircuitLord/BigWalkVRInstaller"](https://raw.githubusercontent.com/yngatech/t3code/pr-assets/configure-fork-upstream-remote/clone-after-destination.png?v=064d1d3f9)

| Before | After |
| --- | --- |
| ![Checkout settings without a default repository
row](https://raw.githubusercontent.com/yngatech/t3code/pr-assets/configure-fork-upstream-remote/settings-before.png?v=064d1d3f9)
| ![Checkout settings with a Default repository
row](https://raw.githubusercontent.com/yngatech/t3code/pr-assets/configure-fork-upstream-remote/settings-after.png?v=064d1d3f9)
|

Mobile gets the same choice at clone time, plus a **Default repository**
row in the thread git sheet so the phone is not a one-way door:

<img
src="https://raw.githubusercontent.com/yngatech/t3code/pr-assets/configure-fork-upstream-remote/mobile-after.png?v=064d1d3f9"
width="320" alt="The fork chooser on iOS with owner avatars">

- `gh-resolved` is not always `base`: with a single remote, `gh repo
set-default` writes an `owner/repo`. That value is parsed and shown
rather than silently overwritten, and the parse is now shared with
`RepositoryIdentityResolver` so the two cannot drift.
- The pin is written with `--replace-all`, since `gh` *adds* resolutions
and a plain write refuses to overwrite a multi-value key (exit 5).
- Only forks send `provider`/`repository` on clone, so every other clone
costs no extra `gh repo view` and behaves exactly as before against an
older server.
- Identity still follows the branch's tracked remote first, per #79; the
pin only decides when a branch has no upstream yet.

- `vp test run apps/server/src/sourceControl apps/server/src/project
packages/shared/src/git.test.ts packages/client-runtime` — 797 tests
passed, including new cases for the `owner/repo` pin, the unset path,
dotted remote names, and a fetch failure leaving the clone intact
- Targeted typecheck, lint, and formatting across server, contracts,
shared, client-runtime, web, and mobile
- Web: clone flow and Settings driven by hand and captured above
- iOS Simulator: fork chooser verified on device; the git sheet row is
typechecked but unexercised, since it needs a thread with real git state
- Two independent agent reviews; findings folded in

---
Written by an agent (Claude Code, claude-opus-5).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:L vouch:trusted PR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant