Files
headquarter/docs/superpowers/specs/2026-05-24-session-branch-selection-design.md
T
alex 10a5c29702 docs: add session branch selection design spec
- Design for branch dropdown in session creation
- New local branch creation at clone time
- Frontend/backend changes overview
2026-05-24 09:18:29 +00:00

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}/branches endpoint that returns all branches and the default branch
  • A clone_repository service 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 active
  • isLoadingBranches: boolean
  • isCreatingNewBranch: boolean — toggled when "Create new branch..." is selected
  • newBranchName: string — the name for the new branch
  • baseBranch: 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 baseBranch to default_branch from the response
  • If the current branch state is not in the list, reset it to default_branch

4. Branch Dropdown

Replace the free-text input with a <select>:

  • Options populated from branches state
  • 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 to default_branch)

6. Form Submission

Update handleCreate to handle new branch creation:

  • If isCreatingNewBranch is true, pass newBranchName and baseBranch to the API
  • The branch parameter sent to the API should be:
    • newBranchName if 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_branch is provided:
    1. Clone the data.branch (base branch) as usual
    2. Run git -C <clone_path> checkout -b <new_branch> to create the local branch
    3. Store new_branch in the branch field of the ToolInstance record

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

  1. Frontend: Test branch dropdown loads correctly, "Create new branch" toggle works, form submission sends correct payload
  2. Backend: Test instance creation with new_branch parameter, verify git command runs correctly
  3. Integration: End-to-end test creating a session with a new branch

Files Changed

  • apps/web/src/api/git_repositories.ts — Add listRepositoryBranches function
  • apps/web/src/pages/sessions.tsx — Replace branch input with dropdown + new branch form
  • apps/api/src/api/tool_instances.py — Extend CreateInstanceRequest and clone logic
  • apps/api/src/services/clone.py — Add create_local_branch helper (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.