# 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`:

```typescript
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:

```python
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.
