Skip to content

[Bug]: Diff panel Working tree/Branch scopes silently empty for every in-place project — getDiffPreview workspace-root guard compares against $HOME in packaged desktop builds #4022

Description

@mikeohuo

Before submitting

  • I searched existing issues and did not find a duplicate.
  • I included enough detail to reproduce or investigate the problem.

Area

apps/server

Steps to reproduce

  1. Install a packaged desktop build (stable 0.0.28, Windows installer). In packaged builds the backend server is spawned with cwd = $HOME (apps/desktop/src/app/DesktopEnvironment.ts: backendCwd: input.isPackaged ? homeDirectory : appRoot — documented in [Bug]: Packaged builds set server cwd to $HOME; ClaudeProvider auxiliary spawns leak it past project bindings #2867), and the spawn passes no --cwd flag, so ServerConfig.cwd resolves to process.cwd() = the home directory. Verified on the live process: the backend child's cwd is C:\Users\T9.
  2. Add any project whose repo lives outside the home directory (e.g. D:\git\<repo>) and run a thread in place (not in a t3-managed worktree).
  3. Open the diff panel and switch the scope dropdown to Working tree or Branch changes.

Expected behavior

The panel shows the working-tree / branch diff of the project's repository, as those scopes describe.

Actual behavior

The panel silently renders empty for every in-place project, regardless of repo size or state. Two-step failure:

  1. ReviewService.getDiffPreview calls assertWorkspaceBoundCwd, which only accepts a cwd inside ServerConfig.cwd or config.worktreesDir. Since ServerConfig.cwd is $HOME in packaged builds, the project cwd (D:\git\<repo>) is rejected in ~2 ms:

    VcsRepositoryDetectionError: VCS repository detection failed in ReviewService.getDiffPreview: D:\git\ABW\game - Review diff preview cwd must stay within the configured workspace root.

  2. The client already knows about this failure mode — apps/web/src/components/DiffPanel.tsx has a dedicated fallback:

    const shouldRetryBranchDiffAtEnvironmentCwd =
      primaryBranchDiffPreview.error?.includes("configured workspace root") === true &&
      serverConfig?.cwd !== undefined &&
      serverConfig.cwd !== activeCwd;

    The retry re-queries with cwd: serverConfig.cwd — i.e. it computes the diff preview against $HOME, which is not a git repository, so the query "succeeds" with no sources and the panel shows nothing. No error ever surfaces to the user.

The only cwds that can pass the guard are t3-managed worktrees under ~/.t3/worktrees, so Working tree / Branch changes scopes only function for worktree threads. On large monorepos this leaves the diff panel with no working scope at all, because the Turn scopes are independently broken there by the checkpoint-capture timeout (#3646).

The guard itself is well-motivated — it's the validation added after #316 (arbitrary client-supplied cwd). The problem is that it validates against the server process's cwd, which in packaged desktop builds is $HOME by design, rather than against the workspace roots of registered projects. #2867 reports the same $HOME-cwd root cause from the provider-probe side; #2441 looks like this same silent-empty diff panel observed without the root cause identified.

Impact

Major degradation or frequent failure

Version or commit

0.0.28 (desktop stable, Windows installer); code paths verified identical at main @ ecb35f7

Environment

Windows 11 Pro (10.0.26200), T3 Code desktop 0.0.28, git 2.54.0.windows.1, provider claudeAgent

Logs or stack traces

server.trace.ndjson — the two spans emitted by one open of the diff panel (primary + fallback):

19:10:13  ws.rpc.review.getDiffPreview  2.0 ms   Failure
  VcsRepositoryDetectionError: VCS repository detection failed in
  ReviewService.getDiffPreview: D:\git\ABW\game - Review diff preview cwd
  must stay within the configured workspace root.
      at .../app.asar/apps/server/dist/bin.mjs:37040:17

19:10:13  ws.rpc.review.getDiffPreview  437.2 ms Success   (fallback at serverConfig.cwd = C:\Users\T9 — not a repo, empty result)

Backend spawn (no --cwd; ServerConfig falls back to process.cwd()):
  "...\t3code\T3 Code (Alpha).exe" ...\app.asar\apps\server\dist\bin.mjs --bootstrap-fd 3
  live process cwd: C:\Users\T9

Workaround

Run the thread in a t3-managed worktree (paths under ~/.t3/worktrees pass the guard). No workaround exists for in-place projects. Suggested direction: validate getDiffPreview's cwd against the registered projects' workspace roots (and thread worktree paths) instead of ServerConfig.cwd, or have the desktop app pass the project roots to the server as allowed boundaries — this would fix the diff panel while keeping the #316 hardening intact. The DiffPanel.tsx fallback should also surface an error instead of silently rendering an empty diff of the wrong directory.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions