Skip to content

feat: configurable branch prefixes for v2 workspaces#4833

Open
Kitenite wants to merge 2 commits into
mainfrom
configurable-branch-prefi
Open

feat: configurable branch prefixes for v2 workspaces#4833
Kitenite wants to merge 2 commits into
mainfrom
configurable-branch-prefi

Conversation

@Kitenite
Copy link
Copy Markdown
Collaborator

@Kitenite Kitenite commented May 22, 2026

Summary

Brings back v1's branch prefix feature for the v2 (host-service) workspace flow — requested by Censys (SUPER-835) for their org-wide v2 trial.

v1 stored prefixes in the desktop's local SQLite and applied them at workspace creation; v2 creates workspaces in packages/host-service, which had no prefix support. This restores full v1 parity: host-local storage, a global default plus per-project overrides, and all four modes (none / github / author / custom).

Behaviour

  • Resolved prefix is prepended as a path segment: prefix/branch-name.
  • Applied to new branches only — auto-generated names and user-typed names that don't already exist. Existing branches and PR checkouts are never re-prefixed.
  • Project override wins over the host-wide default; collision guard drops the prefix if it equals an existing branch name.

Changes

  • @superset/shared — exports BRANCH_PREFIX_MODES / BranchPrefixMode (host-service can't depend on @superset/local-db).
  • host-service DBprojects.branchPrefixMode / branchPrefixCustom columns + single-row host_settings table; migration 0005 (auto-applies on startup).
  • host-serviceresolveProjectBranchPrefix util (cascade + collision guard), settings.branchPrefix.{get,set,gitInfo} router, project.get/setBranchPrefix, and prefix application in workspaces.create.
  • RendererV2GitSettings (host-wide default on /settings/git) and a per-project BranchPrefixSection in V2ProjectSettings.

Plan doc: plans/v2-branch-prefixes.md.

Test Plan

  • typecheck — host-service, shared, desktop
  • lint — clean
  • New branch-prefix.test.ts — 7 tests for cascade + collision; mutation-tested
  • Full host-service tRPC suite — 255 pass
  • Manual: set global + per-project prefixes in v2 settings, create a workspace, confirm branch is namespaced

Open in Stage

Summary by cubic

Adds configurable branch prefixes to v2 workspaces, matching v1: host-wide default and per‑project overrides (none, github, author, custom). Applied only to new branches with collision guards; includes an auto‑applied DB migration and delivers SUPER‑835 for Censys.

  • New Features

    • Host-wide default in host_settings; tRPC settings.branchPrefix.{get,set,gitInfo}; per‑project override via projects.branchPrefixMode/branchPrefixCustom and project.setBranchPrefix.
    • Prefix resolution and application in workspaces.create using resolveProjectBranchPrefix (reads git user.name/gh for author/github, drops prefixes that collide with existing branches).
    • UI: V2GitSettings on /settings/git (global default) and BranchPrefixSection in V2ProjectSettings (project override).
    • Shared: BRANCH_PREFIX_MODES/BranchPrefixMode live in @superset/shared; @superset/local-db re‑exports them to keep a single source of truth.
  • Refactors

    • Removed the 5‑minute GitHub username cache in the host-service resolver to avoid stale values on gh auth switch (the renderer preview still uses a 5‑minute stale time).
    • Extracted a shared BranchPrefixControl used by both V2GitSettings and the project settings section.

Written for commit 2f78050. Summary will update on new commits. Review in cubic

Summary by CodeRabbit

  • New Features

    • Configurable branch prefix modes (none, GitHub username, author name, or custom) with host-wide defaults and optional per-project overrides
    • New global and per-project settings UI to preview and edit branch prefix behavior
    • Branch prefixes automatically applied to newly created branches during workspace creation
  • Bug Fixes / Behavior

    • Collision guard: prefixes are skipped when they would conflict with existing branch names

Review Change Stack

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 22, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 0cd948f9-af61-4778-8cc4-53a2ffecff51

📥 Commits

Reviewing files that changed from the base of the PR and between 071a4c7 and 2f78050.

📒 Files selected for processing (7)
  • apps/desktop/src/renderer/routes/_authenticated/settings/components/BranchPrefixControl/BranchPrefixControl.tsx
  • apps/desktop/src/renderer/routes/_authenticated/settings/components/BranchPrefixControl/index.ts
  • apps/desktop/src/renderer/routes/_authenticated/settings/git/components/V2GitSettings/V2GitSettings.tsx
  • apps/desktop/src/renderer/routes/_authenticated/settings/v2-project/$projectId/components/V2ProjectSettings/components/BranchPrefixSection/BranchPrefixSection.tsx
  • packages/host-service/src/trpc/router/workspace-creation/utils/branch-prefix.ts
  • packages/local-db/src/schema/zod.ts
  • packages/shared/src/workspace-launch/branch.ts
✅ Files skipped from review due to trivial changes (1)
  • apps/desktop/src/renderer/routes/_authenticated/settings/components/BranchPrefixControl/index.ts

📝 Walkthrough

Walkthrough

Implements v2 branch-prefixes: shared mode types, DB migration for host/project settings, prefix-resolution utilities (git/gh identity and collision guard), host-service routers, workspace integration applying prefixes to new branches, renderer global and per-project UI, tests, and docs.

Changes

V2 Configurable Branch Prefixes

Layer / File(s) Summary
Shared mode types and constants
packages/shared/src/workspace-launch/branch.ts, packages/local-db/src/schema/zod.ts
Introduce BRANCH_PREFIX_MODES and BranchPrefixMode and update resolveBranchPrefix typing; re-export types where consumed.
Database schema and migrations
packages/host-service/drizzle/0005_branch_prefix_settings.sql, packages/host-service/drizzle/meta/*, packages/host-service/src/db/schema.ts
Create host_settings table and add branch_prefix_* columns to projects; add migration journal and Drizzle snapshot.
Branch prefix resolution utilities
packages/host-service/src/trpc/router/workspace-creation/utils/branch-prefix.ts
Add getGitAuthorName, getGitHubUsername, resolveGitInfo, and resolveProjectBranchPrefix to derive effective prefix from host/project settings and git/gh identity, with collision suppression.
Prefix resolution tests
packages/host-service/src/trpc/router/workspace-creation/utils/branch-prefix.test.ts
In-memory DB tests for host defaults, project overrides, null vs none semantics, collision suppression, and author-mode resolution.
Host-service tRPC routers
packages/host-service/src/trpc/router/settings/branch-prefix.ts, packages/host-service/src/trpc/router/settings/index.ts, packages/host-service/src/trpc/router/project/project.ts
Add branchPrefixRouter (get/set/gitInfo), mount it in settingsRouter, and add projectRouter.setBranchPrefix + include branchPrefix fields in project get.
Workspace branch naming integration
packages/host-service/src/trpc/router/workspaces/workspaces.ts
Apply resolved project prefix to new branches (typed and auto-generated paths) and re-deduplicate final branch names.
Global git settings component
apps/desktop/src/renderer/routes/_authenticated/settings/git/components/V2GitSettings/*, apps/desktop/src/renderer/routes/_authenticated/settings/git/page.tsx
Add V2GitSettings to fetch/persist host-wide prefix settings, show live preview, and conditionally render when v2 cloud is enabled.
Per-project settings component
apps/desktop/src/renderer/routes/_authenticated/settings/v2-project/.../BranchPrefixSection/*, V2ProjectSettings.tsx
Add BranchPrefixSection delegating to BranchPrefixControl and persist per-project overrides; integrate into project settings UI when host/project present.
BranchPrefixControl UI
apps/desktop/src/renderer/routes/_authenticated/settings/components/BranchPrefixControl/*
New control: mode select (with default sentinel), optional custom prefix input, local state and sanitization, and change callbacks used by global/project UIs.
Settings variant visibility
apps/desktop/src/renderer/routes/_authenticated/settings/utils/settings-search/settings-search.ts
Mark GIT_BRANCH_PREFIX as shared so it is visible in both v1 and v2 settings UIs.
Feature plan documentation
plans/v2-branch-prefixes.md
Add plan describing modes, precedence, storage, collision rules, and implementation touchpoints.

Sequence Diagram(s)

sequenceDiagram
  participant Renderer
  participant HostService
  participant DB
  participant ExecGh as ExecGh/Git

  Renderer->>HostService: GET settings.git.branchPrefix (branchPrefixRouter.get)
  HostService->>DB: SELECT host_settings / projects
  HostService->>ExecGh: resolveGitInfo (gh / git)
  ExecGh-->>HostService: github username / author name
  HostService-->>Renderer: branchPrefix mode + preview info
  Renderer->>HostService: MUTATE settings.branchPrefix.set (persist)
  HostService->>DB: UPSERT host_settings (id=1) or UPDATE project row
  HostService-->>Renderer: success / error
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Possibly related PRs

  • superset-sh/superset#4665: Updates how target host URL context is derived in V2ProjectSettings; intersects with branch-prefix project integration.

Poem

🐰 I munched a carrot, typed a name,

prefixes bloom in branchy fame—
Host or project, choose your tune,
author, github, custom, soon.
New branches spring beneath the moon.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 46.67% 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 clearly and concisely summarizes the main change: adding configurable branch prefixes for v2 workspaces, which is the core feature of this pull request.
Description check ✅ Passed The PR description is comprehensive and well-structured. It includes a clear summary, describes the behavior, explains all changes across multiple packages, and details the testing approach. However, the template sections (Description, Related Issues, Type of Change, Testing, Screenshots, Additional Notes) are not explicitly filled following the template structure.
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 configurable-branch-prefi

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint skipped: no ESLint configuration detected in root package.json. To enable, add eslint to devDependencies.


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.

@capy-ai
Copy link
Copy Markdown

capy-ai Bot commented May 22, 2026

Capy auto-review is paused for this organization because the monthly auto-review limit has been reached. Increase the limit or turn it off in billing settings to resume automatic reviews.

@stage-review
Copy link
Copy Markdown

stage-review Bot commented May 22, 2026

Ready to review this PR? Stage has broken it down into 7 individual chapters for you:

Title
1 Define shared branch prefix types
2 Update host-service database schema
3 Implement branch prefix resolution logic
4 Expose branch prefix settings via tRPC
5 Apply prefixes during workspace creation
6 Build branch prefix UI components
7 Integrate prefix settings into the app
Open in Stage

Chapters generated by Stage for commit 2f78050 on May 24, 2026 5:50am UTC.

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: 1

🧹 Nitpick comments (1)
packages/host-service/src/trpc/router/workspace-creation/utils/branch-prefix.test.ts (1)

56-140: ⚡ Quick win

Add github mode coverage (including fallback behavior).

The suite currently exercises none/custom/author, but not the github path that depends on execGh and fallback-to-author semantics. A focused test here would prevent regressions in the highest-risk branch of the resolver.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@packages/host-service/src/trpc/router/workspace-creation/utils/branch-prefix.test.ts`
around lines 56 - 140, Add tests in branch-prefix.test.ts exercising the
"github" branchPrefixMode for resolveProjectBranchPrefix: (1) a test where
execGh is stubbed to return a GitHub username and ensure
resolveProjectBranchPrefix (called with project: makeProject({ branchPrefixMode:
"github" }), ctx: makeCtx(createTestDb()), and git: gitWithAuthor(null)) returns
the expected GH-based prefix; (2) a test where execGh is stubbed to throw or
return null and ensure the code falls back to author semantics by passing git:
gitWithAuthor("Jane Doe") and asserting the returned prefix equals the
author-derived value; use the same helpers referenced in the suite
(resolveProjectBranchPrefix, execGh stub, gitWithAuthor, makeProject, makeCtx,
createTestDb, setGlobal where needed) to locate and implement the tests.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/host-service/drizzle/0005_branch_prefix_settings.sql`:
- Around line 1-5: The migration currently allows any id and any mode string;
change the host_settings table to enforce a single-row enum-backed record by
adding a CHECK constraint that id = 1 (to guarantee single-row semantics), make
branch_prefix_mode NOT NULL with a CHECK limiting values to the supported enum
(e.g., CHECK(branch_prefix_mode IN ('off','include','prefix'))) and set a
sensible DEFAULT (e.g., 'off'), and tighten branch_prefix_custom (NOT NULL
DEFAULT '' and optionally a conditional CHECK such as CHECK(branch_prefix_mode
<> 'prefix' OR (branch_prefix_custom IS NOT NULL AND branch_prefix_custom <>
'')) ) so invalid combinations are prevented; apply these changes to the CREATE
TABLE for host_settings and any related INSERT/UPDATE paths that assume those
defaults.

---

Nitpick comments:
In
`@packages/host-service/src/trpc/router/workspace-creation/utils/branch-prefix.test.ts`:
- Around line 56-140: Add tests in branch-prefix.test.ts exercising the "github"
branchPrefixMode for resolveProjectBranchPrefix: (1) a test where execGh is
stubbed to return a GitHub username and ensure resolveProjectBranchPrefix
(called with project: makeProject({ branchPrefixMode: "github" }), ctx:
makeCtx(createTestDb()), and git: gitWithAuthor(null)) returns the expected
GH-based prefix; (2) a test where execGh is stubbed to throw or return null and
ensure the code falls back to author semantics by passing git:
gitWithAuthor("Jane Doe") and asserting the returned prefix equals the
author-derived value; use the same helpers referenced in the suite
(resolveProjectBranchPrefix, execGh stub, gitWithAuthor, makeProject, makeCtx,
createTestDb, setGlobal where needed) to locate and implement the tests.
🪄 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: d39c5171-5b22-43c3-8e9b-dc01fc4af6ef

📥 Commits

Reviewing files that changed from the base of the PR and between b2e1da0 and 071a4c7.

📒 Files selected for processing (19)
  • apps/desktop/src/renderer/routes/_authenticated/settings/git/components/V2GitSettings/V2GitSettings.tsx
  • apps/desktop/src/renderer/routes/_authenticated/settings/git/components/V2GitSettings/index.ts
  • apps/desktop/src/renderer/routes/_authenticated/settings/git/page.tsx
  • apps/desktop/src/renderer/routes/_authenticated/settings/utils/settings-search/settings-search.ts
  • apps/desktop/src/renderer/routes/_authenticated/settings/v2-project/$projectId/components/V2ProjectSettings/V2ProjectSettings.tsx
  • apps/desktop/src/renderer/routes/_authenticated/settings/v2-project/$projectId/components/V2ProjectSettings/components/BranchPrefixSection/BranchPrefixSection.tsx
  • apps/desktop/src/renderer/routes/_authenticated/settings/v2-project/$projectId/components/V2ProjectSettings/components/BranchPrefixSection/index.ts
  • packages/host-service/drizzle/0005_branch_prefix_settings.sql
  • packages/host-service/drizzle/meta/0005_snapshot.json
  • packages/host-service/drizzle/meta/_journal.json
  • packages/host-service/src/db/schema.ts
  • packages/host-service/src/trpc/router/project/project.ts
  • packages/host-service/src/trpc/router/settings/branch-prefix.ts
  • packages/host-service/src/trpc/router/settings/index.ts
  • packages/host-service/src/trpc/router/workspace-creation/utils/branch-prefix.test.ts
  • packages/host-service/src/trpc/router/workspace-creation/utils/branch-prefix.ts
  • packages/host-service/src/trpc/router/workspaces/workspaces.ts
  • packages/shared/src/workspace-launch/branch.ts
  • plans/v2-branch-prefixes.md

Comment on lines +1 to +5
CREATE TABLE `host_settings` (
`id` integer PRIMARY KEY DEFAULT 1 NOT NULL,
`branch_prefix_mode` text,
`branch_prefix_custom` text
);
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 | 🟠 Major | ⚡ Quick win

Enforce host_settings invariants at the DB layer.

host_settings is modeled as a single-row enum-backed table, but this migration currently permits any id and any mode string. A stray insert/update can make settings reads nondeterministic or return unsupported mode values.

Suggested migration hardening
 CREATE TABLE `host_settings` (
-	`id` integer PRIMARY KEY DEFAULT 1 NOT NULL,
-	`branch_prefix_mode` text,
+	`id` integer PRIMARY KEY DEFAULT 1 NOT NULL CHECK (`id` = 1),
+	`branch_prefix_mode` text CHECK (
+		`branch_prefix_mode` IN ('none', 'github', 'author', 'custom')
+		OR `branch_prefix_mode` IS NULL
+	),
 	`branch_prefix_custom` text
 );
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
CREATE TABLE `host_settings` (
`id` integer PRIMARY KEY DEFAULT 1 NOT NULL,
`branch_prefix_mode` text,
`branch_prefix_custom` text
);
CREATE TABLE `host_settings` (
`id` integer PRIMARY KEY DEFAULT 1 NOT NULL CHECK (`id` = 1),
`branch_prefix_mode` text CHECK (
`branch_prefix_mode` IN ('none', 'github', 'author', 'custom')
OR `branch_prefix_mode` IS NULL
),
`branch_prefix_custom` text
);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/host-service/drizzle/0005_branch_prefix_settings.sql` around lines 1
- 5, The migration currently allows any id and any mode string; change the
host_settings table to enforce a single-row enum-backed record by adding a CHECK
constraint that id = 1 (to guarantee single-row semantics), make
branch_prefix_mode NOT NULL with a CHECK limiting values to the supported enum
(e.g., CHECK(branch_prefix_mode IN ('off','include','prefix'))) and set a
sensible DEFAULT (e.g., 'off'), and tighten branch_prefix_custom (NOT NULL
DEFAULT '' and optionally a conditional CHECK such as CHECK(branch_prefix_mode
<> 'prefix' OR (branch_prefix_custom IS NOT NULL AND branch_prefix_custom <>
'')) ) so invalid combinations are prevented; apply these changes to the CREATE
TABLE for host_settings and any related INSERT/UPDATE paths that assume those
defaults.

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented May 22, 2026

Greptile Summary

This PR ports the v1 branch-prefix feature to the v2 (host-service) workspace flow, adding a two-tier configuration (host-wide default + per-project override) with four modes (none / github / author / custom), a new host_settings Drizzle migration, tRPC endpoints for getting/setting prefix config, and renderer components for both the global git settings page and per-project settings.

  • The cascade logic (project.branchPrefixMode != null wins, else hostSettings row, else none) and the collision guard (drops prefix if it equals an existing branch name) are implemented correctly and well-tested for most cases.
  • The github mode's authorName fallback fetch is intentional — resolveBranchPrefix uses githubUsername || authorPrefix || null, so getGitAuthorName is legitimately needed even in that mode.
  • The typed-branch path correctly keeps the original plan.startPoint after prefix application (the branch was confirmed non-existent, so the startPoint was already resolved from baseBranch/HEAD independently of the branch name).

Confidence Score: 4/5

Safe to merge — the core cascade and collision-guard logic is correct, the migration is additive, and the existing workspace creation path for PR checkouts and existing branches is unaffected.

The new prefix resolution code is well-structured and the happy path is thoroughly covered. The two non-test findings (double-prefix on manually-typed names and misleading Custom UI state when the prefix is cleared) are edge cases that won't break any existing workspace creation flows. The github mode gap in the test suite is the main thing that could hide a regression silently.

workspaces.ts around the typed-branch prefix block (double-prefix guard) and branch-prefix.test.ts (missing github mode coverage).

Important Files Changed

Filename Overview
packages/host-service/src/trpc/router/workspace-creation/utils/branch-prefix.ts Core prefix resolution logic with cascade (project → host default) and collision guard; github mode correctly fetches authorName as fallback; module-level githubUsernameCache singleton is appropriate for single-user desktop use
packages/host-service/src/trpc/router/workspaces/workspaces.ts Prefix applied in both typed-branch and auto-gen paths; typed-branch path correctly guards !plan.usedExistingBranch; listBranchNames added to the parallel fetch; plan spread keeps original startPoint which is correct since the branch was confirmed non-existent
packages/host-service/src/trpc/router/workspace-creation/utils/branch-prefix.test.ts 7 tests covering cascade, collision, and author mode; github mode (with its caching and authorName fallback) is not covered at all
packages/host-service/src/trpc/router/settings/branch-prefix.ts Host-wide default stored via upsert on id=1 singleton row; gitInfo query surfaces author/username for UI preview; synchronous Drizzle .run() / .get() API used consistently
apps/desktop/src/renderer/routes/_authenticated/settings/git/components/V2GitSettings/V2GitSettings.tsx Host-wide default UI; handleCustomPrefixBlur can persist mode:custom with a null prefix when the input is cleared, leaving the select showing Custom while no prefix is actually applied
apps/desktop/src/renderer/routes/_authenticated/settings/v2-project/$projectId/components/V2ProjectSettings/components/BranchPrefixSection/BranchPrefixSection.tsx Per-project override with use-global-default option; same handleCustomPrefixBlur pattern as V2GitSettings — clears prefix but keeps mode:custom

Sequence Diagram

sequenceDiagram
    participant UI as Renderer
    participant PR as tRPC router
    participant DB as host-service SQLite
    participant WS as workspaces.create
    participant BP as resolveProjectBranchPrefix
    participant GH as gh CLI / git config
    UI->>PR: settings.branchPrefix.set
    PR->>DB: UPSERT host_settings
    UI->>PR: project.setBranchPrefix
    PR->>DB: UPDATE projects
    WS->>DB: SELECT project
    WS->>BP: resolveProjectBranchPrefix
    BP->>DB: SELECT host_settings
    alt "mode == github"
        BP->>GH: gh api user (cached 5 min)
        BP->>GH: git config user.name (fallback)
    else "mode == author"
        BP->>GH: git config user.name
    end
    BP->>BP: collision guard
    BP-->>WS: prefix or undefined
    alt typed branch and not usedExistingBranch
        WS->>WS: deduplicateBranchName(prefix/branch)
    else auto-gen branch
        WS->>WS: deduplicateBranchName(prefix/candidate)
    end
Loading

Comments Outside Diff (1)

  1. packages/host-service/src/trpc/router/workspace-creation/utils/branch-prefix.test.ts, line 1290-1374 (link)

    P2 github mode has no test coverage

    The suite exercises none, host-default fallback, project override, null-mode inheritance, none-override, collision guard, and author mode — but github mode is never tested. github mode has two unique behaviours that are easy to regress: (1) it fetches the GitHub username via the gh CLI and caches the result in a module-level singleton (githubUsernameCache), and (2) it falls back to authorPrefix when the username is unavailable (githubUsername || authorPrefix || null in resolveBranchPrefix). A test with a stubbed execGh returning a username, and another where execGh throws (forcing the authorName fallback path), would cover both branches and the cache behaviour.

    Prompt To Fix With AI
    This is a comment left during a code review.
    Path: packages/host-service/src/trpc/router/workspace-creation/utils/branch-prefix.test.ts
    Line: 1290-1374
    
    Comment:
    **`github` mode has no test coverage**
    
    The suite exercises `none`, host-default fallback, project override, null-mode inheritance, `none`-override, collision guard, and `author` mode — but `github` mode is never tested. `github` mode has two unique behaviours that are easy to regress: (1) it fetches the GitHub username via the `gh` CLI and caches the result in a module-level singleton (`githubUsernameCache`), and (2) it falls back to `authorPrefix` when the username is unavailable (`githubUsername || authorPrefix || null` in `resolveBranchPrefix`). A test with a stubbed `execGh` returning a username, and another where `execGh` throws (forcing the `authorName` fallback path), would cover both branches and the cache behaviour.
    
    How can I resolve this? If you propose a fix, please make it concise.
Prompt To Fix All With AI
Fix the following 3 code review issues. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 3
packages/host-service/src/trpc/router/workspace-creation/utils/branch-prefix.test.ts:1290-1374
**`github` mode has no test coverage**

The suite exercises `none`, host-default fallback, project override, null-mode inheritance, `none`-override, collision guard, and `author` mode — but `github` mode is never tested. `github` mode has two unique behaviours that are easy to regress: (1) it fetches the GitHub username via the `gh` CLI and caches the result in a module-level singleton (`githubUsernameCache`), and (2) it falls back to `authorPrefix` when the username is unavailable (`githubUsername || authorPrefix || null` in `resolveBranchPrefix`). A test with a stubbed `execGh` returning a username, and another where `execGh` throws (forcing the `authorName` fallback path), would cover both branches and the cache behaviour.

### Issue 2 of 3
packages/host-service/src/trpc/router/workspaces/workspaces.ts:839-856
**Double-prefix when user types a name that already includes the prefix**

`planBranchSource` is called with the raw `typedBranch` (e.g. `censys/feature`) before the prefix is known. If that branch doesn't exist, `usedExistingBranch` is `false` and the prefix is unconditionally prepended, producing `censys/censys/feature`. A user who is aware of their prefix and manually namespaces their branch will silently end up on a double-nested ref. The PR description doesn't address this case. A simple guard — checking whether `typedBranch` already starts with `${prefix}/` before prepending — would prevent the double-prefix without changing any other behaviour.

### Issue 3 of 3
apps/desktop/src/renderer/routes/_authenticated/settings/git/components/V2GitSettings/V2GitSettings.tsx:113-117
**Clearing the custom prefix persists `mode:"custom"` with a null value**

When the user deletes the text in the custom-prefix input and blurs, `sanitizeSegment("")` returns `""`, which is then coerced to `null` by `sanitized || null`. The mutation saves `{ mode: "custom", customPrefix: null }`. `resolveBranchPrefix` returns `null` for this state (`customPrefix || null`), so no prefix is applied — but the Select still shows "Custom". A user who clears the field expecting "Custom" to stop applying a prefix will see the correct preview (just `branch-name`) but the mode label is misleading. The same pattern is present in `BranchPrefixSection.tsx`. Switching `mode` back to `"none"` when the sanitized value is empty would make the saved state match the user's intent.

Reviews (1): Last reviewed commit: "feat(host-service): configurable branch ..." | Re-trigger Greptile

Comment on lines +839 to +856
// Namespace newly-created branches under the configured
// prefix. A typed branch that resolves to an existing ref is
// checked out as-is and never re-prefixed.
if (!plan.usedExistingBranch) {
const prefix = await resolveProjectBranchPrefix({
ctx,
project: localProject,
git,
existingBranches: existing,
});
if (prefix) {
resolvedBranch = deduplicateBranchName(
`${prefix}/${resolvedBranch}`,
existing,
);
plan = { ...plan, branch: resolvedBranch };
}
}
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.

P2 Double-prefix when user types a name that already includes the prefix

planBranchSource is called with the raw typedBranch (e.g. censys/feature) before the prefix is known. If that branch doesn't exist, usedExistingBranch is false and the prefix is unconditionally prepended, producing censys/censys/feature. A user who is aware of their prefix and manually namespaces their branch will silently end up on a double-nested ref. The PR description doesn't address this case. A simple guard — checking whether typedBranch already starts with ${prefix}/ before prepending — would prevent the double-prefix without changing any other behaviour.

Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/host-service/src/trpc/router/workspaces/workspaces.ts
Line: 839-856

Comment:
**Double-prefix when user types a name that already includes the prefix**

`planBranchSource` is called with the raw `typedBranch` (e.g. `censys/feature`) before the prefix is known. If that branch doesn't exist, `usedExistingBranch` is `false` and the prefix is unconditionally prepended, producing `censys/censys/feature`. A user who is aware of their prefix and manually namespaces their branch will silently end up on a double-nested ref. The PR description doesn't address this case. A simple guard — checking whether `typedBranch` already starts with `${prefix}/` before prepending — would prevent the double-prefix without changing any other behaviour.

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

Comment on lines +113 to +117
return (
<div className="p-6 max-w-4xl w-full">
<div className="mb-8">
<h2 className="text-xl font-semibold">Git &amp; worktrees</h2>
<p className="text-sm text-muted-foreground mt-1">
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.

P2 Clearing the custom prefix persists mode:"custom" with a null value

When the user deletes the text in the custom-prefix input and blurs, sanitizeSegment("") returns "", which is then coerced to null by sanitized || null. The mutation saves { mode: "custom", customPrefix: null }. resolveBranchPrefix returns null for this state (customPrefix || null), so no prefix is applied — but the Select still shows "Custom". A user who clears the field expecting "Custom" to stop applying a prefix will see the correct preview (just branch-name) but the mode label is misleading. The same pattern is present in BranchPrefixSection.tsx. Switching mode back to "none" when the sanitized value is empty would make the saved state match the user's intent.

Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/desktop/src/renderer/routes/_authenticated/settings/git/components/V2GitSettings/V2GitSettings.tsx
Line: 113-117

Comment:
**Clearing the custom prefix persists `mode:"custom"` with a null value**

When the user deletes the text in the custom-prefix input and blurs, `sanitizeSegment("")` returns `""`, which is then coerced to `null` by `sanitized || null`. The mutation saves `{ mode: "custom", customPrefix: null }`. `resolveBranchPrefix` returns `null` for this state (`customPrefix || null`), so no prefix is applied — but the Select still shows "Custom". A user who clears the field expecting "Custom" to stop applying a prefix will see the correct preview (just `branch-name`) but the mode label is misleading. The same pattern is present in `BranchPrefixSection.tsx`. Switching `mode` back to `"none"` when the sanitized value is empty would make the saved state match the user's intent.

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

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.

2 issues found across 19 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="packages/host-service/src/trpc/router/workspaces/workspaces.ts">

<violation number="1" location="packages/host-service/src/trpc/router/workspaces/workspaces.ts:842">
P2: Typed branch creation can incorrectly create a suffixed new branch when the prefixed branch already exists, because existence is checked before applying the prefix.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment on lines +842 to +856
if (!plan.usedExistingBranch) {
const prefix = await resolveProjectBranchPrefix({
ctx,
project: localProject,
git,
existingBranches: existing,
});
if (prefix) {
resolvedBranch = deduplicateBranchName(
`${prefix}/${resolvedBranch}`,
existing,
);
plan = { ...plan, branch: resolvedBranch };
}
}
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.

P2: Typed branch creation can incorrectly create a suffixed new branch when the prefixed branch already exists, because existence is checked before applying the prefix.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/host-service/src/trpc/router/workspaces/workspaces.ts, line 842:

<comment>Typed branch creation can incorrectly create a suffixed new branch when the prefixed branch already exists, because existence is checked before applying the prefix.</comment>

<file context>
@@ -828,12 +829,31 @@ export const workspacesRouter = router({
+					// Namespace newly-created branches under the configured
+					// prefix. A typed branch that resolves to an existing ref is
+					// checked out as-is and never re-prefixed.
+					if (!plan.usedExistingBranch) {
+						const prefix = await resolveProjectBranchPrefix({
+							ctx,
</file context>
Suggested change
if (!plan.usedExistingBranch) {
const prefix = await resolveProjectBranchPrefix({
ctx,
project: localProject,
git,
existingBranches: existing,
});
if (prefix) {
resolvedBranch = deduplicateBranchName(
`${prefix}/${resolvedBranch}`,
existing,
);
plan = { ...plan, branch: resolvedBranch };
}
}
if (!plan.usedExistingBranch) {
const prefix = await resolveProjectBranchPrefix({
ctx,
project: localProject,
git,
existingBranches: existing,
});
if (prefix) {
const prefixedBranch = `${prefix}/${resolvedBranch}`;
const prefixedPlan = await planBranchSource(
git,
prefixedBranch,
input.baseBranch,
);
if (prefixedPlan.usedExistingBranch) {
resolvedBranch = prefixedBranch;
plan = prefixedPlan;
} else {
resolvedBranch = deduplicateBranchName(prefixedBranch, existing);
plan = { ...plan, branch: resolvedBranch };
}
}
}

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 22, 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

- Cut the 5-min GitHub-username cache in the host-service resolver. Workspace
  creates aren't a hot path and the cache was a stale-account footgun on
  `gh auth switch`. The renderer preview already has a 5-min `staleTime`.
- Single source for `BranchPrefixMode` — `@superset/local-db` re-exports from
  `@superset/shared/workspace-launch`. No more drift risk between the two.
- Extract `BranchPrefixControl` used by both `V2GitSettings` (host-wide) and
  `BranchPrefixSection` (per-project, `showDefault`). Empty custom-prefix on
  blur no longer fires a misleading `mode=custom, customPrefix=null` write.
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