Skip to content

feat(host-service): v2 workspace creation fallback to origin/main#3361

Merged
Kitenite merged 4 commits into
mainfrom
workspace-creation-fallback
Apr 12, 2026
Merged

feat(host-service): v2 workspace creation fallback to origin/main#3361
Kitenite merged 4 commits into
mainfrom
workspace-creation-fallback

Conversation

@Kitenite
Copy link
Copy Markdown
Collaborator

@Kitenite Kitenite commented Apr 11, 2026

Summary

  • Add resolveStartPoint utility that prefers origin/<branch> over local <branch> when creating worktrees, with HEAD as ultimate fallback
  • Detect the repo's actual default branch via git symbolic-ref instead of hardcoding "main"
  • Fetch only the resolved remote ref before worktree creation (git fetch origin <branch>) for freshness without full-repo fetch overhead
  • Add --no-track to prevent new branches from auto-tracking the remote ref
  • Design doc with cross-product comparison (VS Code, T3Code, GitHub Desktop, Superset v1)

Test plan

  • Unit tests for resolveStartPoint (7 tests passing)
  • Typecheck passes
  • Manual: create workspace in desktop app, verify log shows origin/main resolved and fetched
  • Manual: test offline/no-auth scenario — should fall back gracefully

Summary by cubic

Workspace creation now starts from the freshest branch by preferring origin/<branch> or the repo’s remote default branch, with local and HEAD fallbacks. It fetches only that ref before creating the worktree and disables upstream tracking to avoid accidental pushes.

  • New Features
    • Added resolveStartPoint to choose origin/<branch> → local <branch>HEAD; when no baseBranch, detect default via git symbolic-ref refs/remotes/origin/HEAD --short with a "main" fallback.
    • If the start point is origin/*, fetch just that ref (git fetch origin <branch> --quiet --no-tags) before creation; proceed gracefully if the fetch fails.
    • Always pass --no-track to git worktree add to prevent auto-tracking.
    • Co-located unit tests for resolveStartPoint, including the cold-start HEAD fallback case.

Written for commit 3cc80ba. Summary will update on new commits.

Summary by CodeRabbit

  • New Features

    • Enhanced workspace creation with intelligent branch resolution—now prefers remote branches, falls back to local branches, and defaults to HEAD when needed for improved branch selection.
  • Tests

    • Added comprehensive test coverage for the new branch resolution logic.

When resolveStartPoint resolves to an origin/* ref, fetch just that
single branch before creating the worktree to ensure we branch from
the latest remote state. Fails gracefully if offline or auth expired.

Also updates the design doc with cross-product comparison (VS Code,
T3Code, GitHub Desktop) and documents the fetch strategy.
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 11, 2026

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 7aa186e4-4e87-4b06-946e-bd087f86d9a1

📥 Commits

Reviewing files that changed from the base of the PR and between fd4d7cf and 3cc80ba.

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

📝 Walkthrough

Walkthrough

A new workspace start-point resolver was added to determine the appropriate Git reference for v2 workspace creation. The resolver prefers remote-tracking refs, falls back to local refs, and ultimately uses HEAD. Default branch resolution is performed when baseBranch is not provided, and targeted fetches are attempted for remote-tracking refs during workspace creation.

Changes

Cohort / File(s) Summary
Documentation
packages/host-service/WORKSPACE_CREATION_FALLBACK.md
New markdown file documenting the workspace start-point resolver behavior, fallback logic, and implementation details.
Start-Point Resolver Implementation
packages/host-service/src/trpc/router/workspace-creation/utils/resolve-start-point.ts
New exported function resolveStartPoint() that resolves Git refs with fallback logic: checks origin/<branch>, falls back to local <branch>, uses HEAD as final fallback. Includes helper functions for default branch name resolution and ref existence checking.
Resolver Tests
packages/host-service/src/trpc/router/workspace-creation/utils/resolve-start-point.test.ts
New test suite with mock Git interface covering resolution precedence, default branch handling, fallback scenarios, and edge cases like whitespace normalization.
Workspace Creation Integration
packages/host-service/src/trpc/router/workspace-creation/workspace-creation.ts
Modified to use resolveStartPoint() for determining base ref, added targeted git fetch for remote-tracking refs, and updated worktree creation with --no-track flag to prevent automatic remote tracking.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 A Git Ref Odyssey

Through origin's branches, the resolver hops near,
Then local paths checked with a methodical cheer,
When neither exists, to HEAD we will flee,
With fetches and worktrees in harmony! 🌿

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly describes the main change: adding fallback logic to prefer origin/main (or other remote branches) in v2 workspace creation.
Description check ✅ Passed The PR description includes all major required sections: Summary with clear feature description, Test plan with test status, Related context via cross-product comparison, and Additional Notes. The description is well-structured and comprehensive.

✏️ 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 workspace-creation-fallback

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 11, 2026

Greptile Summary

This PR improves V2 workspace creation by replacing the hardcoded baseBranch || \"HEAD\" fallback with a principled 3-tier resolution strategy: prefer origin/<branch> (freshest remote-tracking ref), then local <branch>, then HEAD. It also detects the repo's actual default branch via git symbolic-ref instead of hardcoding \"main\", adds a targeted single-ref fetch for freshness right before worktree creation, and adds --no-track to prevent auto-tracking of the remote ref.

Key changes:

  • New resolveStartPoint utility with 3-tier fallback and dynamic default branch detection via git symbolic-ref refs/remotes/origin/HEAD --short
  • Targeted git fetch origin <branch> only when resolved to a remote-tracking ref — avoids full-repo fetch overhead
  • --no-track added to git worktree add to prevent new branches from auto-tracking origin/<branch>
  • 7 unit tests covering all major resolution paths
  • Comprehensive design doc comparing this approach to VS Code, T3Code, and GitHub Desktop implementations

The implementation is well-designed, with graceful error handling at every git call — a failed fetch falls back to the locally cached ref, and a failed symbolic-ref defaults to \"main\".

Confidence Score: 4/5

Safe to merge — logic is sound with graceful error handling at every step; two minor P2 suggestions remain.

The core resolution logic is correct, well-tested across 7 scenarios, and fails gracefully at every git call. The only two non-blocking items are a stylistic inconsistency in the fetch API call (array vs. raw) and one missing test case for the HEAD fallback when no baseBranch is provided and no refs exist. Neither affects production correctness.

No files require special attention; the minor style note in workspace-creation.ts is optional cleanup.

Important Files Changed

Filename Overview
packages/host-service/src/trpc/router/workspace-creation/utils/resolve-start-point.ts New utility implementing a clean 3-tier fallback (origin/ → local → HEAD) with correct use of rev-parse and symbolic-ref; error handling is appropriate and all paths are covered.
packages/host-service/src/trpc/router/workspace-creation/workspace-creation.ts Integrates resolveStartPoint and targeted fetch before worktree creation; the git.fetch() call uses an array-as-options-bag pattern that works but is inconsistent with the rest of the file's git.raw() usage.
packages/host-service/test/resolve-start-point.test.ts 7 targeted unit tests covering the main resolution paths; missing one scenario (no baseBranch + symbolic-ref fails + no main ref → HEAD fallback).
packages/host-service/WORKSPACE_CREATION_FALLBACK.md Thorough design doc comparing VS Code, T3Code, GitHub Desktop, and Superset v1 approaches; accurately reflects the implementation choices made in this PR.

Sequence Diagram

sequenceDiagram
    participant UI as UI/Renderer
    participant WC as workspace-creation.ts
    participant RSP as resolveStartPoint()
    participant Git as simple-git

    UI->>WC: create(baseBranch?, ...)
    WC->>RSP: resolveStartPoint(git, baseBranch)
    
    alt baseBranch provided
        RSP->>Git: rev-parse --verify origin/<baseBranch>
        alt origin/<baseBranch> exists
            Git-->>RSP: OK
            RSP-->>WC: ref=origin/<baseBranch>, resolvedFrom=remote-tracking
        else not found
            RSP->>Git: rev-parse --verify <baseBranch>
            alt <baseBranch> exists locally
                Git-->>RSP: OK
                RSP-->>WC: ref=<baseBranch>, resolvedFrom=local
            else not found
                RSP-->>WC: ref=HEAD, resolvedFrom=fallback
            end
        end
    else no baseBranch
        RSP->>Git: symbolic-ref refs/remotes/origin/HEAD --short
        alt symbolic-ref succeeds
            Git-->>RSP: origin/main strips to main
        else fails
            RSP-->>RSP: default = main
        end
        RSP->>Git: rev-parse --verify origin/<default>
        Note over RSP,Git: same 3-tier chain as above
        RSP-->>WC: resolved ref
    end

    alt startPoint starts with origin/
        WC->>Git: fetch origin <branch> --quiet --no-tags
        alt fetch fails
            Git-->>WC: error caught, logged as warning
        end
    end

    WC->>Git: worktree add --no-track -b <branchName> <path> <startPoint>
    Git-->>WC: worktree created
    WC-->>UI: success
Loading

Reviews (1): Last reviewed commit: "feat(host-service): add targeted fetch b..." | Re-trigger Greptile

Comment on lines +397 to +399
try {
await git.fetch(["origin", remoteBranch, "--quiet", "--no-tags"]);
} catch (err) {
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 Consider using git.raw() for fetch consistency

git.fetch() in simple-git has an overload that accepts TaskOptions (a string[]), so passing ["origin", remoteBranch, "--quiet", "--no-tags"] as the first argument is interpreted as the options bag — not as positional remote / branch arguments. This means the resulting command should be git fetch origin <remoteBranch> --quiet --no-tags, which is what's intended.

However, using git.raw() is unambiguous and consistent with every other git call in this file (symbolic-ref, rev-parse, worktree add):

Suggested change
try {
await git.fetch(["origin", remoteBranch, "--quiet", "--no-tags"]);
} catch (err) {
try {
await git.raw(["fetch", "origin", remoteBranch, "--quiet", "--no-tags"]);
} catch (err) {

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Keeping git.fetch() here — it is simple-git's typed API for this operation. The other git.raw() calls in this file are for commands without typed wrappers (worktree add, symbolic-ref, rev-parse with ^{commit}). Using the typed API when available is the more correct choice.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 11, 2026

🧹 Preview Cleanup Complete

The following preview resources have been cleaned up:

  • ✅ Neon database branch
  • ✅ Electric Fly.io app

Thank you for your contribution! 🎉

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 4 files

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.

Actionable comments posted: 2

🧹 Nitpick comments (2)
packages/host-service/src/trpc/router/workspace-creation/utils/resolve-start-point.ts (1)

24-35: Deduplicate default-branch resolution with existing helper.

Line 24–35 duplicates logic that already exists in packages/host-service/src/trpc/router/git/utils/git-helpers.ts (getDefaultBranchName). Reusing it will keep behavior centralized.

♻️ Suggested refactor
 import type { SimpleGit } from "simple-git";
+import { getDefaultBranchName } from "../../git/utils/git-helpers";
@@
 async function resolveDefaultBranchName(git: SimpleGit): Promise<string> {
-	try {
-		const ref = await git.raw([
-			"symbolic-ref",
-			"refs/remotes/origin/HEAD",
-			"--short",
-		]);
-		return ref.trim().replace(/^origin\//, "");
-	} catch {
-		return "main";
-	}
+	return (await getDefaultBranchName(git)) ?? "main";
 }
🤖 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/utils/resolve-start-point.ts`
around lines 24 - 35, The function resolveDefaultBranchName duplicates logic
already implemented in getDefaultBranchName; replace resolveDefaultBranchName's
implementation to call and return the existing getDefaultBranchName(git) helper
(import it from packages/host-service/src/trpc/router/git/utils/git-helpers.ts)
and remove the duplicated try/catch logic so branch-resolution behavior is
centralized via getDefaultBranchName.
packages/host-service/src/trpc/router/workspace-creation/workspace-creation.ts (1)

409-415: Consider documenting the project's minimum Git version requirement.

The --no-track flag for git worktree add has been available since Git 2.16.0 (January 2018), so it's widely supported across modern environments and poses negligible runtime risk. However, the project doesn't document an explicit minimum Git version. Since the codebase relies on git operations through simple-git, consider adding a minimum Git version requirement (e.g., ≥ 2.16) to setup docs or a .tool-versions file to set expectations for contributors and deployments.

🤖 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 409 - 415, The code uses git's worktree add with the "--no-track"
flag in the git.raw call inside the workspace creation logic (the array
including "worktree","add","--no-track","-b"), so add a documented minimum Git
version requirement (recommend >= 2.16.0) to the repository setup: update
README/CONTRIBUTING or add a .tool-versions entry to state Git >= 2.16.0 and
mention it in the environment/setup section so contributors and CI are aware of
the dependency.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/host-service/test/resolve-start-point.test.ts`:
- Around line 1-2: Move the test for resolveStartPoint so it is co-located with
its source file resolve-start-point.ts: create/rename it to
resolve-start-point.test.ts in the same directory as resolve-start-point.ts,
update the import to load resolveStartPoint from the local module (e.g. import {
resolveStartPoint } from './resolve-start-point'), and remove any references to
the shared/top-level test directory so the test follows the project's
co-location convention.

In `@packages/host-service/WORKSPACE_CREATION_FALLBACK.md`:
- Around line 45-49: The markdown file triggers markdownlint MD040 because
several fenced code blocks lack language identifiers; update each referenced
fenced block (the blocks that contain the git snippets such as the one with "git
config: branch.<name>.gh-merge-base", the blocks at the ranges 62-66, 69-73,
88-92, 122-133, and 241-251) by adding an appropriate language hint (e.g.,
```text or ```bash) after the opening backticks so each fenced code block
declares its language identifier.

---

Nitpick comments:
In
`@packages/host-service/src/trpc/router/workspace-creation/utils/resolve-start-point.ts`:
- Around line 24-35: The function resolveDefaultBranchName duplicates logic
already implemented in getDefaultBranchName; replace resolveDefaultBranchName's
implementation to call and return the existing getDefaultBranchName(git) helper
(import it from packages/host-service/src/trpc/router/git/utils/git-helpers.ts)
and remove the duplicated try/catch logic so branch-resolution behavior is
centralized via getDefaultBranchName.

In
`@packages/host-service/src/trpc/router/workspace-creation/workspace-creation.ts`:
- Around line 409-415: The code uses git's worktree add with the "--no-track"
flag in the git.raw call inside the workspace creation logic (the array
including "worktree","add","--no-track","-b"), so add a documented minimum Git
version requirement (recommend >= 2.16.0) to the repository setup: update
README/CONTRIBUTING or add a .tool-versions entry to state Git >= 2.16.0 and
mention it in the environment/setup section so contributors and CI are aware of
the dependency.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: a46ca37d-a6e2-4c86-aeb1-aa1d82aa9039

📥 Commits

Reviewing files that changed from the base of the PR and between 9dc347c and fd4d7cf.

📒 Files selected for processing (4)
  • packages/host-service/WORKSPACE_CREATION_FALLBACK.md
  • packages/host-service/src/trpc/router/workspace-creation/utils/resolve-start-point.ts
  • packages/host-service/src/trpc/router/workspace-creation/workspace-creation.ts
  • packages/host-service/test/resolve-start-point.test.ts

Comment thread packages/host-service/test/resolve-start-point.test.ts Outdated
Comment on lines +45 to +49
```
1. git config: branch.<name>.gh-merge-base
2. git symbolic-ref refs/remotes/<remote>/HEAD (remote default branch)
3. Candidates ["main", "master"] — check local refs/heads/ then remote refs/remotes/
```
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Add language identifiers to fenced code blocks.

Line 45, Line 62, Line 69, Line 88, Line 122, and Line 241 start fenced blocks without a language, which triggers markdownlint MD040.

📝 Example fix pattern
-```
+```text
 1. git config: branch.<name>.gh-merge-base
 2. git symbolic-ref refs/remotes/<remote>/HEAD
 3. Candidates ["main", "master"] ...
-```
+```

Also applies to: 62-66, 69-73, 88-92, 122-133, 241-251

🧰 Tools
🪛 markdownlint-cli2 (0.22.0)

[warning] 45-45: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/host-service/WORKSPACE_CREATION_FALLBACK.md` around lines 45 - 49,
The markdown file triggers markdownlint MD040 because several fenced code blocks
lack language identifiers; update each referenced fenced block (the blocks that
contain the git snippets such as the one with "git config:
branch.<name>.gh-merge-base", the blocks at the ranges 62-66, 69-73, 88-92,
122-133, and 241-251) by adding an appropriate language hint (e.g., ```text or
```bash) after the opening backticks so each fenced code block declares its
language identifier.

…lback case

Moves the test next to its source per AGENTS.md co-location convention
and adds a missing test for the cold-start HEAD fallback path (no
baseBranch provided, symbolic-ref fails, no default branch exists).
@Kitenite Kitenite merged commit c6a0fc4 into main Apr 12, 2026
14 of 15 checks passed
@Kitenite Kitenite deleted the workspace-creation-fallback branch April 12, 2026 00:08
MocA-Love pushed a commit to MocA-Love/superset that referenced this pull request Apr 13, 2026
…perset-sh#3361)

* Workspace creation

* feat(host-service): add targeted fetch before worktree creation

When resolveStartPoint resolves to an origin/* ref, fetch just that
single branch before creating the worktree to ensure we branch from
the latest remote state. Fails gracefully if offline or auth expired.

Also updates the design doc with cross-product comparison (VS Code,
T3Code, GitHub Desktop) and documents the fetch strategy.

* Lint

* test(host-service): co-locate resolve-start-point test + add HEAD fallback case

Moves the test next to its source per AGENTS.md co-location convention
and adds a missing test for the cold-start HEAD fallback path (no
baseBranch provided, symbolic-ref fails, no default branch exists).
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