Skip to content

fix: six multi-repo workspace features work again on orchestrator v2 - #348

Merged
LoganRupe merged 16 commits into
multi-repo-workspacesfrom
feature/multi-repo/v2-gap-restore
Oct 14, 2026
Merged

LoganRupe merged 16 commits into
multi-repo-workspacesfrom
feature/multi-repo/v2-gap-restore

Conversation

@LoganRupe

@LoganRupe LoganRupe commented Oct 14, 2026 •

Copy link
Copy Markdown
Owner

Problem

The move to orchestrator v2 (#338) dropped six things multi-repo workspaces did on the pre-v2 branch: editors opening the .code-workspace, setup running in every repository, files and @ search covering every listed folder, a base branch per repository, agents reaching listed folders that aren't repositories, and mobile review per repository.

Change

One commit per feature, each restoring it on the v2 code paths:

Scope and approval

This is my fork. These are regressions against features that already shipped on the pre-v2 multi-repo branch, and each issue above links the original PRs.

Verification

Before/after video, side by side: left is multi-repo-workspaces @ 0d5799c703, right is this branch. The fixture is a project folder team with team.code-workspace listing web, ../api (repo with a release branch) and ../design (plain folder).

v2-gap-restore-part1.mp4

What the video shows, per feature:

  1. Editors. Before, VS Code opens the bare team folder. After, it opens "team (Workspace)" with Web App, API and Design. A New worktree thread opens "t3-… (Workspace)" with the same three folders.
  2. Setup. Before, cp: /.env: No such file or directory and "Workspace preparation failed". After, [API] and [Web App] copied .env into …, one setup terminal per repository, and the agent replies.
  3. Files and @ search, web and iOS. Before, the Files panel shows web, notes.md and team.code-workspace, and @guide finds nothing. After, it shows API, Design and Web App, opens Design › guide.md, and @guide offers guide.md from Design.
  4. Base branch, web and iOS. Before, there are no per-repository controls, and on iOS Start task stays disabled. After, API is set to release, and the run's terminal shows API at release only with release.ts.
  5. Agents. Before, Claude lists team, api and attachments and says the guide is outside its working directories. After, it lists design as a working folder and quotes the guide.
  6. Mobile review. Before, an empty Turn 1 with Changes and Uncommitted greyed out. After, Uncommitted · Web App, then Repository › API shows API's README change.

The sub-agents that wrote each commit report their focused tests and typechecks (server, web, client-runtime, mobile) passing. I did not rerun them for this PR.

Not covered here: the mobile Git menu still disables "Review changes" when the project folder isn't a repository, so the video opens Review through the thread's /review link (noted in #347). Building the iOS dev client on Xcode 27.0 also needed a local edit to T3LayoutMetricsView.swift, because that code wants the iOS 27.1 SDK. The edit is not in this branch.

Changes by Claude sub-agents orchestrated from T3 Code; video, issues and this PR by Claude Opus 5.5 in Claude Code.

LoganRupe and others added 15 commits October 14, 2026 17:06
In a workspace folder that is not itself a repository, the mobile review
screen asked for Uncommitted and Changes in the folder and got nothing
or an error. It now lists the workspace's repositories, the same RPC the
web diff panel uses, and compares the picked one.

- The diff menu gains a Repository submenu. It defaults to the folder's
  own repository when the workspace lists it, else the first.
- Git sections of a non-root repository carry its name in the title and
  its path in the section id, so review comments and viewed files stay
  with the repository they came from.
- Cached sections are tagged with their checkout, so switching never
  shows the previous repository's diff while the new one loads.
- Turn diffs are unchanged: they already cover every repository, with
  paths prefixed by repository.

The default rule lives in client-runtime as resolveReviewRepository.
Ordinary checkouts list no repositories and look exactly as before.
"Open in" handed every editor the bare project folder, so VS Code never showed repositories
outside the project (`../api`) or the folder names a workspace file gives. Isolated runs opened
their container as a plain folder with the same losses.

The server now decides which workspace file a folder opens as and reports it on
vcs.listRepositories: the folder's single `.code-workspace` (the one discovery already reads),
or, for an isolated run, a copy written beside the container with each repository pointed at
its worktree, other folders at the project, and settings kept. The Open in picker, its
keybinding and SSH deep links pass that file on; only editors that open workspace files (the
VS Code family) get it, the rest keep the folder.

`t3 app path/to/team.code-workspace` adds the folder holding the file, through the same
projectFolderForPath the add-project pickers use.

Written with Claude Opus 5.5 in Claude Code, running inside T3 Code.
…o isolated run

An isolated run of a multi-repo workspace got one setup script run in the
container folder, so a script written for a repository, such as copying
.env from the original checkout, skipped every repository.

The runner now reads the isolated run's layout and runs the script once per
repository, in that repository's worktree, with T3CODE_REPO_ROOT set to the
checkout it came from. The root repository keeps the plain terminal id and
the others get one of their own, hashed when a long path would run over the
limit. Output lines on the setup card carry the repository name, the card
names each repository that failed, cancel closes every setup terminal, and
a script that is not async fails the preparation when any repository does.
Settle scripts and MCP handoffs go through the same runner. A single
checkout behaves as before.
…ace-file' into feature/multi-repo/v2-gap-restore
…isolated run

Every repository of a multi-repo isolated run started from its own default
branch, with no way to start one from another branch.

The worktree launch strategy gains repositoryBaseRefs, a base ref per
repository by the relative path the project lists. The worktree builder
starts a listed repository from that ref, fetching it first when the run
starts from origin, and fails the run when the ref does not resolve. The
project folder's own repository takes the strategy's base ref, the branch
the regular selector shows. Base refs are checked in their own repository
before the launch, naming the repository in the error.

The web composer and thread panel show a repositories menu beside the
branch controls with a branch list per repository and a Default branch
item that clears a pick; it also carries the start-from-origin switch when
no single branch selector is there. Picks live in memory until the run
starts. Agents can pass repositoryBaseRefs to t3_thread_launch and
scheduled tasks accept it through the same contract. Mobile keeps the
server defaults.
…launch' into feature/multi-repo/v2-gap-restore

# Conflicts:
#	apps/server/src/git/WorkspaceWorktrees.test.ts
#	apps/web/src/components/ChatView.tsx
#	docs/user/source-control.md
…and the repositories RPC

A .code-workspace project only exposed its Git repositories. A listed folder
that is not a repository was invisible, and a file in a listed folder outside
the project could not be served as an asset.

WorkspaceRepositories.describe now yields every listed folder that exists on
disk, git or not, with the file's name and its absolute path; isolated runs
record where each folder is reached. vcs.listRepositories returns them as
folders. The runtime policy hands the plain folders to adapters: Claude, Codex,
Antigravity and OpenCode grant the ones outside the cwd, and every adapter
names them in the runtime instructions, which now ask multi-root sessions for
paths T3 Code can open. Asset URLs for a workspace file inside a listed folder
are served from that folder, and from nothing else.
… folder a workspace lists

A .code-workspace project's files panel, @ search, command palette file picker
and file previews only covered the project folder, and a listed folder that is
not a Git repository was invisible everywhere.

The client now derives its file roots from the folders the repositories RPC
returns, named the way the file names them, with same-named folders told apart
by a parent segment. The files tree shows one row per root; rows resolve to
their real folder for open, reveal, right-click actions and drags, and a row
that only groups same-named roots acts on the folder they share. Searches fan
out per root through one derived atom and interleave the results. The preview
panel reads, edits, previews and names breadcrumbs through the root that holds
the file, so a chat link to an absolute path in a listed folder opens there.
An ordinary project keeps a single root and looks exactly as before.

The roots helper lives in client-runtime so mobile can consume the same
contract. The user guide explains what the files panel shows.
…iles

The merge of per-root search results is a pure function now, so its
interleaving, labelling and truncation have a direct test.
…ders' into feature/multi-repo/v2-gap-restore

# Conflicts:
#	apps/server/src/ws.ts
#	docs/user/source-control.md
#	packages/contracts/src/git.ts
On mobile, a .code-workspace project's files screen and @ search only
covered the thread's folder, so listed folders elsewhere were missing.

The files tree now uses the roots web uses: one top-level folder per
listed folder, named the way the workspace file names them, with
same-named folders grouped under a parent segment. Rows open by the path
chat links use: relative inside the project folder, absolute in any
other listed folder. The file header names the listed folder a file sits
in. @ search fans out per root and merges the results, and a pick is
mentioned by that same path. Opening a file from the tree or the tablet
inspector keeps the leading slash of an absolute path.

mergeRootSearchResults moves from web's queries into client-runtime so
both clients merge the same way. The repositories query mobile review
already made is now one hook the files surfaces share. Ordinary projects
keep a single root and look as before.
…-repo worktree

Mobile's new task flow could not set the per-repository base branches web
offers, and in a workspace folder that is not itself a repository it could
not start a new worktree at all: the folder has no branches, so Start stayed
disabled.

In New worktree mode a multi-repo workspace now shows a repositories
control beside the branch. It lists every repository but the folder's own,
each with its pick or Default branch, and opens that repository's branch
list with a Default branch row that clears the pick. Picks live in the
draft's workspace selection, travel on the queued task, survive editing a
pending task, and reach the launch as repositoryBaseRefs. Picks for
repositories the workspace no longer lists are dropped.

A workspace folder that is no repository hides the branch control, carries
the start-from-origin switch in the repositories screen, and starts with
HEAD as the folder's base, as web does; the server reads no base for it.

The branch choices, pick updates and the folder-has-a-branch rule live in
client-runtime; web's menu uses the same branch choices.
…ace-folders' into feature/multi-repo/v2-gap-restore
…utside the project

Mobile review of an outside repository (../api) failed with "Review diff
preview cwd must stay within the configured workspace root": the review
guard only trusted the server cwd, managed worktree folders and project
roots. It now also trusts every repository and folder a registered
project's workspace file lists, the same set asset access serves.
@github-actions github-actions Bot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:XXL labels Oct 14, 2026
@LoganRupe

Copy link
Copy Markdown
Owner Author

Part 2 of gap video:

v2-gap-restore-part2.mp4

CI's lint job runs vp check and knip. ws.ts had one unformatted call, and
knip flagged outsideWorkspaceRepositoryPaths, now only used inside git.ts,
plus the two layer make constants that base already left unused.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XXL 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.

2 participants