Skip to content

fix(host-service): place v2 worktrees under ~/.superset/worktrees/<projectId>#3669

Merged
Kitenite merged 4 commits into
mainfrom
in-the-v2-version-please-find-where-were-creating-new-worktree-into-vs-the-pattern-we-had-in-v1
Apr 24, 2026
Merged

fix(host-service): place v2 worktrees under ~/.superset/worktrees/<projectId>#3669
Kitenite merged 4 commits into
mainfrom
in-the-v2-version-please-find-where-were-creating-new-worktree-into-vs-the-pattern-we-had-in-v1

Conversation

@Kitenite
Copy link
Copy Markdown
Collaborator

@Kitenite Kitenite commented Apr 23, 2026

Summary

  • v2 host-service was creating worktrees at <repo>/.worktrees/<branch>; switch to ~/.superset/worktrees/<projectId>/<branch> to mirror v1 desktop and match v2's existing ~/.superset/repos/<projectId> layout.
  • listWorktreeBranches now detects Superset-managed worktrees via the local workspaces table (plus the new root, for orphan adoption) instead of a hardcoded path prefix.
  • No migration: legacy worktrees at <repo>/.worktrees/* stay in place and keep working because their workspaces.worktreePath rows still match in detection. Only new worktrees use the new location.

Test plan

  • Create a new workspace (branch path) in v2 — verify it lands at ~/.superset/worktrees/<projectId>/<branch>.
  • Create a new workspace from a PR — same location.
  • Check out an existing branch into a new workspace — same location.
  • Open a project that already has legacy worktrees at <repo>/.worktrees/<branch> — verify they still show in the Worktree tab and can be opened.
  • Create a new worktree, kill mid-way before the workspaces row lands, reopen project — orphan should appear in Worktree tab and be adoptable.

Summary by cubic

Move v2 host-service worktrees to ~/.superset/worktrees/<projectId>/<branch> to mirror v1 and match ~/.superset/repos/<projectId>, keeping them outside the repo. Legacy worktrees still work; no migration needed.

  • Bug Fixes
    • Detect managed worktrees via the local workspaces table and the new managed root (to surface orphans for adoption).
    • Ensure parent directories exist before creating a worktree.
    • Build paths from projectId; update worktree discovery and adoption accordingly.

Written for commit c822cb0. Summary will update on new commits.

Summary by CodeRabbit

  • Chores

    • Moved worktree storage to a managed, project-scoped location for better isolation and organization.
    • Improved discovery to reliably list worktrees per project and mark on-disk orphans as available for adoption.
    • Ensured parent directories are created before worktree create/checkout operations to reduce path-related errors.
  • Documentation

    • Updated adopt endpoint docs to reflect the new on-disk layout and adoption behavior.

…ctId>

Mirror v1 desktop's convention of keeping worktrees outside the primary
checkout tree, and match v2's existing ~/.superset/repos/<projectId>
layout for symmetry. Detection switches to the local `workspaces` table
(plus the new root for orphan adoption), so legacy worktrees at
<repo>/.worktrees/ keep working without a migration.
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 23, 2026

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 14709051-49ad-495f-bc61-9d1a9e581dd0

📥 Commits

Reviewing files that changed from the base of the PR and between dfb326f and c822cb0.

📒 Files selected for processing (1)
  • packages/host-service/src/trpc/router/workspace-creation/workspace-creation.ts

📝 Walkthrough

Walkthrough

Worktree resolution and discovery were moved to a Superset-managed per-project directory at ~/.superset/worktrees/<projectId>/. Resolution now requires projectId; discovery classifies worktrees as owned if listed in the DB or located under the managed root. Parent directories are created before worktree operations.

Changes

Cohort / File(s) Summary
Worktree Management
packages/host-service/src/trpc/router/workspace-creation/workspace-creation.ts
Reworked safeResolveWorktreePath to accept projectId and enforce containment under ~/.superset/worktrees/<projectId>/; listWorktreeBranches refactored to accept ctx + projectId, builds knownPaths from DB rows and treats on-disk paths under the managed root as adoptable/owned; router calls updated to pass ctx/projectId; ensure parent directories exist via mkdirSync(..., { recursive: true }) before git worktree add/checkout.

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant HostService as HostService (router)
  participant DB as Workspaces DB
  participant FS as Filesystem
  participant Git as Git

  Client->>HostService: request (list/create/adopt) with projectId
  HostService->>DB: query workspaces for projectId (build knownPaths)
  HostService->>FS: safeResolveWorktreePath(projectId, requestedPath)
  alt resolved path not under managed root or invalid
    HostService-->>Client: error (path traversal or invalid)
  else resolved path under managed root
    alt path missing on disk
      HostService->>FS: mkdirSync(parentDir, {recursive:true})
      HostService->>Git: git worktree add / checkout -> create worktree
    else path present on disk
      HostService->>Git: git checkout / adopt flow
    end
    HostService->>DB: update or create workspace row as needed
    HostService-->>Client: response (branches/status)
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

"I hopped from repo nooks to a tidy tree,
Per-project burrows where worktrees can be,
I make the dirs, I check the roots,
Adopt and create with careful hoots —
A rabbit's patchwork, neat and free." 🐇🌿

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 40.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately and specifically describes the main change: moving v2 worktrees to ~/.superset/worktrees/, which is the primary objective of this pull request.
Description check ✅ Passed The pull request description comprehensively covers the required template sections including Summary, Test plan, and additional context from the auto-generated summary. All key changes and testing scenarios are documented.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch in-the-v2-version-please-find-where-were-creating-new-worktree-into-vs-the-pattern-we-had-in-v1

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented Apr 23, 2026

Greptile Summary

This PR relocates Superset-managed git worktrees from <repo>/.worktrees/<branch> to ~/.superset/worktrees/<projectId>/<branch>, mirroring the v1 desktop layout and aligning with the existing ~/.superset/repos/<projectId> convention. The change is backward-compatible: legacy worktrees with existing workspaces DB rows continue to show up in the Worktree tab via the new knownPaths lookup, and the new managedRoot prefix check catches orphans at the new location.

  • New path helpers supersetWorktreesRoot() and projectWorktreesRoot(projectId) replace the old repoPath-relative logic in safeResolveWorktreePath.
  • listWorktreeBranches now accepts ctx and projectId instead of repoPath, building a knownPaths set from all workspaces DB rows for the project to recognize both old and new worktree locations; orphans at the new ~/.superset/worktrees/<projectId>/ root are caught by the prefix check.
  • mkdirSync(dirname(worktreePath), { recursive: true }) is added before each git worktree add call across create, checkout (PR path), and checkout (branch path) to ensure the parent directory exists.
  • Legacy orphans (worktrees on disk at <repo>/.worktrees/ with no DB row) are intentionally not detected after this change — documented in the code comment and PR description.

Confidence Score: 4/5

Safe to merge; the path relocation is correct, backward-compatible, and well-guarded. One minor error-handling improvement would harden the UX but is non-blocking.

The core logic is sound: new worktrees land at the correct location, legacy worktrees are preserved via the DB knownPaths lookup, orphan detection at the new root works correctly, and path traversal protection is maintained. worktreePath is NOT NULL in the schema so no null-handling issues. The only actionable concern is that the three mkdirSync calls have no error handling, which would produce raw Node errors on permission/disk failures rather than structured TRPCErrors — a P2 polish issue that does not affect the primary flow.

No files require special attention beyond the three unguarded mkdirSync calls in workspace-creation.ts.

Important Files Changed

Filename Overview
packages/host-service/src/trpc/router/workspace-creation/workspace-creation.ts Relocates v2 worktrees from <repo>/.worktrees/<branch> to ~/.superset/worktrees/<projectId>/<branch>, adds DB-backed orphan detection in listWorktreeBranches, and adds mkdirSync for the new nested directory structure; backward-compatible with legacy worktrees via knownPaths DB lookup

Sequence Diagram

sequenceDiagram
    participant Client
    participant Router as workspaceCreationRouter
    participant FS as FileSystem
    participant Git
    participant DB as SQLite (workspaces)
    participant Cloud as Cloud API

    Client->>Router: create / checkout (projectId, branch)
    Router->>Router: safeResolveWorktreePath(projectId, branch)<br/>→ ~/.superset/worktrees/<projectId>/<branch>
    Router->>FS: mkdirSync(dirname(worktreePath), { recursive: true })
    Router->>Git: git worktree add <worktreePath> <branch>
    Router->>Cloud: ensureV2Host + v2Workspace.create
    Router->>DB: INSERT workspaces (worktreePath = newPath)
    Router->>Client: { workspace, terminals, warnings }

    Client->>Router: searchBranches / adopt (projectId)
    Router->>DB: SELECT worktreePath WHERE projectId = ? → knownPaths
    Router->>Git: git worktree list --porcelain
    loop each worktree entry
        Router->>Router: if knownPaths.has(path) OR path.startsWith(managedRoot/)<br/>→ add to worktreeMap
    end
    Router->>Client: { worktreeMap, checkedOutBranches }
Loading
Prompt To Fix All With AI
This is a comment left during a code review.
Path: packages/host-service/src/trpc/router/workspace-creation/workspace-creation.ts
Line: 789

Comment:
**Unhandled `mkdirSync` failure**

`mkdirSync` can throw (e.g. permission denied, disk full, a file already exists at that path) and is called outside any try/catch here. If it throws, the mutation will propagate a raw `EACCES`/`ENOSPC`/`EEXIST` Node error to the client rather than a structured `TRPCError`. The same pattern is repeated at lines 1133 and 1259.

Consider wrapping each call so failures surface with a clear, user-friendly message:

```ts
try {
  mkdirSync(dirname(worktreePath), { recursive: true });
} catch (err) {
  clearProgress(input.pendingId);
  throw new TRPCError({
    code: "INTERNAL_SERVER_ERROR",
    message: `Failed to create worktree directory: ${err instanceof Error ? err.message : String(err)}`,
  });
}
```

How can I resolve this? If you propose a fix, please make it concise.

Reviews (1): Last reviewed commit: "fix(host-service): place worktrees under..." | Re-trigger Greptile

localProject.id,
branchName,
);
mkdirSync(dirname(worktreePath), { recursive: true });
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Unhandled mkdirSync failure

mkdirSync can throw (e.g. permission denied, disk full, a file already exists at that path) and is called outside any try/catch here. If it throws, the mutation will propagate a raw EACCES/ENOSPC/EEXIST Node error to the client rather than a structured TRPCError. The same pattern is repeated at lines 1133 and 1259.

Consider wrapping each call so failures surface with a clear, user-friendly message:

try {
  mkdirSync(dirname(worktreePath), { recursive: true });
} catch (err) {
  clearProgress(input.pendingId);
  throw new TRPCError({
    code: "INTERNAL_SERVER_ERROR",
    message: `Failed to create worktree directory: ${err instanceof Error ? err.message : String(err)}`,
  });
}
Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/host-service/src/trpc/router/workspace-creation/workspace-creation.ts
Line: 789

Comment:
**Unhandled `mkdirSync` failure**

`mkdirSync` can throw (e.g. permission denied, disk full, a file already exists at that path) and is called outside any try/catch here. If it throws, the mutation will propagate a raw `EACCES`/`ENOSPC`/`EEXIST` Node error to the client rather than a structured `TRPCError`. The same pattern is repeated at lines 1133 and 1259.

Consider wrapping each call so failures surface with a clear, user-friendly message:

```ts
try {
  mkdirSync(dirname(worktreePath), { recursive: true });
} catch (err) {
  clearProgress(input.pendingId);
  throw new TRPCError({
    code: "INTERNAL_SERVER_ERROR",
    message: `Failed to create worktree directory: ${err instanceof Error ? err.message : String(err)}`,
  });
}
```

How can I resolve this? If you propose a fix, please make it concise.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/host-service/src/trpc/router/workspace-creation/workspace-creation.ts (1)

202-226: ⚠️ Potential issue | 🟠 Major

Normalize Git and Node.js paths before ownership checks.

currentPath from git worktree list --porcelain uses forward slashes on Windows, while Node.js path.resolve() produces backslashes. The direct comparisons knownPaths.has(currentPath) and currentPath.startsWith(managedRoot + sep) will fail on Windows, causing Superset-managed worktrees to be missed.

Proposed normalization
-import { dirname, join, resolve, sep } from "node:path";
+import { dirname, join, normalize, resolve, sep } from "node:path";
 function projectWorktreesRoot(projectId: string): string {
 	return resolve(supersetWorktreesRoot(), projectId);
 }
+
+function pathKey(path: string): string {
+	const normalized = normalize(resolve(path));
+	return process.platform === "win32" ? normalized.toLowerCase() : normalized;
+}
 
 function safeResolveWorktreePath(
-	const managedRoot = projectWorktreesRoot(projectId);
+	const managedRootKey = pathKey(projectWorktreesRoot(projectId));
 	const knownPaths = new Set<string>(
 		ctx.db
 			.select({ path: workspaces.worktreePath })
 			.from(workspaces)
 			.where(eq(workspaces.projectId, projectId))
 			.all()
-			.map((w) => w.path),
+			.map((w) => pathKey(w.path)),
 	);
@@
 			} else if (line.startsWith("branch refs/heads/") && currentPath) {
 				const branch = line.slice("branch refs/heads/".length).trim();
 				if (!branch) continue;
 				checkedOutBranches.add(branch);
+				const currentPathKey = pathKey(currentPath);
 				if (
-					knownPaths.has(currentPath) ||
-					currentPath.startsWith(managedRoot + sep)
+					knownPaths.has(currentPathKey) ||
+					currentPathKey.startsWith(managedRootKey + sep)
 				) {
 					worktreeMap.set(branch, currentPath);
 				}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@packages/host-service/src/trpc/router/workspace-creation/workspace-creation.ts`
around lines 202 - 226, currentPath from git.raw can contain POSIX slashes on
Windows so comparisons against Node paths fail; normalize paths before checks by
creating a normalizedCurrentPath and normalizedManagedRoot (use path.normalize
and also replace '/' with path.sep or use path.resolve) and then use
normalizedCurrentPath in knownPaths.has(...) and
normalizedCurrentPath.startsWith(normalizedManagedRoot + sep); also use
normalizedCurrentPath when inserting into worktreeMap or any subsequent path
comparisons so currentPath, managedRoot, knownPaths, and the startsWith check
are all using the same normalized form.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In
`@packages/host-service/src/trpc/router/workspace-creation/workspace-creation.ts`:
- Around line 202-226: currentPath from git.raw can contain POSIX slashes on
Windows so comparisons against Node paths fail; normalize paths before checks by
creating a normalizedCurrentPath and normalizedManagedRoot (use path.normalize
and also replace '/' with path.sep or use path.resolve) and then use
normalizedCurrentPath in knownPaths.has(...) and
normalizedCurrentPath.startsWith(normalizedManagedRoot + sep); also use
normalizedCurrentPath when inserting into worktreeMap or any subsequent path
comparisons so currentPath, managedRoot, knownPaths, and the startsWith check
are all using the same normalized form.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: e68ca10e-6d90-466d-ad2a-d09ec40344db

📥 Commits

Reviewing files that changed from the base of the PR and between dfa14bf and 0380ef2.

📒 Files selected for processing (1)
  • packages/host-service/src/trpc/router/workspace-creation/workspace-creation.ts

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 23, 2026

🚀 Preview Deployment

🔗 Preview Links

Service Status Link
Neon Database (Neon) View Branch
Vercel API (Vercel) Open Preview
Vercel Web (Vercel) Open Preview
Vercel Marketing (Vercel) Open Preview
Vercel Admin (Vercel) Open Preview
Vercel Docs (Vercel) Open Preview

Preview updates automatically with new commits

Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 1 file

…ind-where-were-creating-new-worktree-into-vs-the-pattern-we-had-in-v1

# Conflicts:
#	packages/host-service/src/trpc/router/workspace-creation/workspace-creation.ts
@Kitenite Kitenite merged commit 48ba63a into main Apr 24, 2026
12 of 13 checks passed
@Kitenite Kitenite deleted the in-the-v2-version-please-find-where-were-creating-new-worktree-into-vs-the-pattern-we-had-in-v1 branch April 24, 2026 00:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant