- Design for branch dropdown in session creation - New local branch creation at clone time - Frontend/backend changes overview
5.9 KiB
Session Branch Selection with New Branch Creation
Summary
Replace the free-text branch input in the session creation form with a dropdown of available branches from the repository. Add the ability to create a new local branch at clone time by selecting "Create new branch..." from the dropdown.
Context
The current session creation UI (apps/web/src/pages/sessions.tsx) has a free-text input for the branch name when "Clone fresh copy" mode is selected. Users must manually type the branch name, which is error-prone and doesn't show what branches are available.
The backend already has:
- A
GET /projects/{project_id}/repositories/{repo_id}/branchesendpoint that returns all branches and the default branch - A
clone_repositoryservice that clones a specific branch - Branch creation APIs for the original repository
Design
Frontend Changes
1. Branch API Integration
Add a new API function in apps/web/src/api/git_repositories.ts:
export interface Branch {
name: string;
is_default: boolean;
last_commit: string | null;
}
export interface BranchesResponse {
branches: Branch[];
default_branch: string;
}
export async function listRepositoryBranches(
projectId: string,
repoId: string
): Promise<BranchesResponse> {
const response = await apiClient.get(
`/projects/${projectId}/repositories/${repoId}/branches`
);
return response.data;
}
2. UI State Management
In apps/web/src/pages/sessions.tsx, add state for:
branches:Branch[]— loaded when a repository is selected and clone mode is activeisLoadingBranches:booleanisCreatingNewBranch:boolean— toggled when "Create new branch..." is selectednewBranchName:string— the name for the new branchbaseBranch:string— the base branch for the new branch
3. Branch Loading
When a repository is selected and clone mode is "clone", fetch branches:
- Call
listRepositoryBranches(selectedProject, selectedRepo) - Set
baseBranchtodefault_branchfrom the response - If the current
branchstate is not in the list, reset it todefault_branch
4. Branch Dropdown
Replace the free-text input with a <select>:
- Options populated from
branchesstate - Default branch marked visually:
"main (default)" - Last option:
"Create new branch..."(disabled separator style or as a real option) - When selected, set
isCreatingNewBranch = true
5. New Branch Form
When isCreatingNewBranch is true, show:
- New branch name input (required, validated for valid git branch name)
- Base branch dropdown (populated from
branches, defaulting todefault_branch)
6. Form Submission
Update handleCreate to handle new branch creation:
- If
isCreatingNewBranchis true, passnewBranchNameandbaseBranchto the API - The
branchparameter sent to the API should be:newBranchNameif creating a new branch- The selected existing branch otherwise
Backend Changes
1. Update CreateInstanceRequest
In apps/api/src/api/tool_instances.py, extend the request model:
class CreateInstanceRequest(BaseModel):
model_config = {"extra": "ignore"}
tool_type_id: str = Field(description="UUID of the tool type to instantiate")
display_name: str | None = Field(default=None, description="Optional display name")
clone_mode: str = Field(default="mount", description="'mount' or 'clone'")
branch: str | None = Field(default="main", description="Branch to clone")
new_branch: str | None = Field(default=None, description="Create a new branch from 'branch' after clone")
2. Update Clone Logic
In create_instance, after cloning:
- If
data.new_branchis provided:- Clone the
data.branch(base branch) as usual - Run
git -C <clone_path> checkout -b <new_branch>to create the local branch - Store
new_branchin thebranchfield of the ToolInstance record
- Clone the
3. Update clone_repository Service
No changes needed — it already clones a specific branch. The new branch creation happens after clone.
4. Database Schema
No changes needed — the existing branch field on ToolInstance can store the new branch name.
Data Flow
User selects repo + "Clone fresh copy"
→ Frontend fetches branches from GET /branches
→ User selects "Create new branch..."
→ User fills: newBranchName="feature-x", baseBranch="dev"
→ Frontend sends: { branch: "dev", new_branch: "feature-x", ... }
→ Backend clones "dev" branch
→ Backend runs: git checkout -b feature-x
→ Instance record stores branch="feature-x"
→ Container starts with the new branch checked out
Error Handling
- Branch fetch fails: Show error, fallback to free-text input
- Invalid branch name: Frontend validation (regex for valid git branch names)
- New branch creation fails: Backend returns 400 with git error message
- Branch already exists locally: Backend handles gracefully (git checkout -b will fail if branch exists)
Testing
- Frontend: Test branch dropdown loads correctly, "Create new branch" toggle works, form submission sends correct payload
- Backend: Test instance creation with
new_branchparameter, verify git command runs correctly - Integration: End-to-end test creating a session with a new branch
Files Changed
apps/web/src/api/git_repositories.ts— AddlistRepositoryBranchesfunctionapps/web/src/pages/sessions.tsx— Replace branch input with dropdown + new branch formapps/api/src/api/tool_instances.py— ExtendCreateInstanceRequestand clone logicapps/api/src/services/clone.py— Addcreate_local_branchhelper (optional)
Trade-offs
- Local branch only: The new branch is created in the cloned workspace only, not pushed to the remote. This is intentional — it's a disposable work branch.
- No branch deletion: When the instance is deleted, the branch is lost with the clone. This matches the "disposable" mental model.