# Session Branch Selection Implementation Plan

> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.

**Goal:** Replace free-text branch input with a dropdown of available branches and add "Create new branch" functionality in the session creation form.

**Architecture:** Frontend fetches branches from existing API, displays them in a dropdown with a "Create new branch..." option. When creating a new branch, frontend sends both base branch and new branch name to backend. Backend clones the base branch then creates a local branch in the cloned workspace.

**Tech Stack:** React + TypeScript (frontend), FastAPI + Python (backend), Git via subprocess

---

## File Structure

- `apps/web/src/api/git_repositories.ts` — Add `listRepositoryBranches` API function
- `apps/web/src/pages/sessions.tsx` — Replace branch input with dropdown + new branch form
- `apps/api/src/api/tool_instances.py` — Extend `CreateInstanceRequest`, add local branch creation
- `apps/web/src/api/sessions.ts` — Update `createInstance` signature to accept `newBranch`

---

### Task 1: Add Branch Listing API to Frontend

**Files:**
- Modify: `apps/web/src/api/git_repositories.ts`

- [ ] **Step 1: Add Branch types and listRepositoryBranches function**

Add after the existing imports and before `export interface CommitHistoryEntry`:

```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;
}
```

- [ ] **Step 2: Commit**

```bash
git add apps/web/src/api/git_repositories.ts
git commit -m "feat: add branch listing API function"
```

---

### Task 2: Update Backend Request Model and Clone Logic

**Files:**
- Modify: `apps/api/src/api/tool_instances.py`

- [ ] **Step 1: Extend CreateInstanceRequest with new_branch field**

Change the `CreateInstanceRequest` class (around line 55-64):

```python
class CreateInstanceRequest(BaseModel):
    """Request body for creating a tool instance."""

    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 for the instance")
    clone_mode: str = Field(default="mount", description="Repository access mode: 'mount' or 'clone'")
    branch: str | None = Field(default="main", description="Branch to clone (when clone_mode='clone')")
    new_branch: str | None = Field(default=None, description="Create a new local branch after cloning")
```

- [ ] **Step 2: Add local branch creation after clone**

After the clone block (around line 248), add:

```python
        # Create new local branch if requested
        if data.clone_mode == "clone" and data.new_branch:
            try:
                result = subprocess.run(
                    ["git", "-C", repo_path, "checkout", "-b", data.new_branch],
                    capture_output=True,
                    text=True,
                )
                if result.returncode != 0:
                    logger.error("Failed to create branch %s: %s", data.new_branch, result.stderr)
                    raise RuntimeError(f"Failed to create branch: {result.stderr}")
                logger.info("Created local branch %s in cloned repository", data.new_branch)
            except Exception as exc:
                logger.exception("Failed to create local branch: %s", exc)
                raise HTTPException(
                    status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
                    detail=f"Failed to create local branch: {exc}"
                )
```

- [ ] **Step 3: Update ToolInstance record to store new branch name**

Change the instance creation (around line 321):

```python
            branch=data.new_branch if data.new_branch else (data.branch if data.clone_mode == "clone" else None),
```

- [ ] **Step 4: Commit**

```bash
git add apps/api/src/api/tool_instances.py
git commit -m "feat: support creating local branch during session creation

- Add new_branch field to CreateInstanceRequest
- Run git checkout -b after cloning when new_branch is provided
- Store new branch name in ToolInstance record"
```

---

### Task 3: Update Frontend API to Support newBranch

**Files:**
- Modify: `apps/web/src/api/sessions.ts`

- [ ] **Step 1: Update createInstance signature**

Change the function signature (around line 44):

```typescript
export async function createInstance(
  projectId: string,
  repoId: string,
  toolTypeId: string,
  displayName?: string,
  cloneMode?: string,
  branch?: string,
  newBranch?: string
): Promise<ToolInstance> {
  const response = await apiClient.post(
    `/projects/${projectId}/repositories/${repoId}/instances`,
    {
      tool_type_id: toolTypeId,
      display_name: displayName,
      clone_mode: cloneMode || "mount",
      branch: branch || undefined,
      new_branch: newBranch || undefined,
    }
  );
  return response.data;
}
```

- [ ] **Step 2: Commit**

```bash
git add apps/web/src/api/sessions.ts
git commit -m "feat: add newBranch parameter to createInstance"
```

---

### Task 4: Update Session Creation UI

**Files:**
- Modify: `apps/web/src/pages/sessions.tsx`

- [ ] **Step 1: Add new imports**

Add to existing imports:

```typescript
import { listRepositoryBranches, type Branch } from "../api/git_repositories";
```

- [ ] **Step 2: Add state variables**

Add after `const [sshKeys, setSshKeys] = useState<SSHKey[]>([]);` (around line 44):

```typescript
  const [branches, setBranches] = useState<Branch[]>([]);
  const [isLoadingBranches, setIsLoadingBranches] = useState(false);
  const [isCreatingNewBranch, setIsCreatingNewBranch] = useState(false);
  const [newBranchName, setNewBranchName] = useState("");
  const [baseBranch, setBaseBranch] = useState("");
```

- [ ] **Step 3: Add branch loading effect**

Add after the `loadSshKeys` effect (around line 117):

```typescript
  useEffect(() => {
    const loadBranches = async () => {
      if (!selectedRepo || !selectedProject || cloneMode !== "clone") {
        setBranches([]);
        setIsCreatingNewBranch(false);
        setNewBranchName("");
        setBaseBranch("");
        return;
      }
      setIsLoadingBranches(true);
      try {
        const data = await listRepositoryBranches(selectedProject, selectedRepo);
        setBranches(data.branches);
        const defaultBranch = data.default_branch;
        setBaseBranch(defaultBranch);
        if (!branch || !data.branches.find((b) => b.name === branch)) {
          setBranch(defaultBranch);
        }
      } catch {
        setBranches([]);
      } finally {
        setIsLoadingBranches(false);
      }
    };
    void loadBranches();
  }, [selectedRepo, selectedProject, cloneMode]);
```

- [ ] **Step 4: Replace branch input with dropdown**

Replace the branch input section (around lines 686-696):

```tsx
                {cloneMode === "clone" && (
                  <>
                    <label className="form-field">
                      Branch
                      {isLoadingBranches ? (
                        <span className="muted">Loading branches...</span>
                      ) : (
                        <select
                          value={isCreatingNewBranch ? "__new__" : branch}
                          onChange={(e) => {
                            const value = e.target.value;
                            if (value === "__new__") {
                              setIsCreatingNewBranch(true);
                              setNewBranchName("");
                            } else {
                              setIsCreatingNewBranch(false);
                              setBranch(value);
                              setBaseBranch(value);
                            }
                          }}
                        >
                          {branches.map((b) => (
                            <option key={b.name} value={b.name}>
                              {b.name} {b.is_default ? "(default)" : ""}
                            </option>
                          ))}
                          <option value="__new__">Create new branch...</option>
                        </select>
                      )}
                    </label>

                    {isCreatingNewBranch && (
                      <>
                        <label className="form-field">
                          New Branch Name
                          <input
                            type="text"
                            value={newBranchName}
                            onChange={(e) => setNewBranchName(e.target.value)}
                            placeholder="feature/my-new-branch"
                            required
                          />
                        </label>
                        <label className="form-field">
                          Base Branch
                          <select
                            value={baseBranch}
                            onChange={(e) => setBaseBranch(e.target.value)}
                          >
                            {branches.map((b) => (
                              <option key={b.name} value={b.name}>
                                {b.name} {b.is_default ? "(default)" : ""}
                              </option>
                            ))}
                          </select>
                        </label>
                      </>
                    )}

                    {selectedRepo && (
                      <div className="form-field ssh-key-info">
                        {(() => {
                          const repo = repositories.find((r) => r.id === selectedRepo);
                          if (!repo) return null;
                          if (repo.ssh_key_id) {
                            const key = sshKeys.find((k) => k.id === repo.ssh_key_id);
                            return (
                              <span className="success-text">
                                SSH key: {key?.name || "Assigned"}
                              </span>
                            );
                          }
                          return (
                            <span className="warning-text">
                              No SSH key assigned to this repository. Clone mode requires an SSH key.
                            </span>
                          );
                        })()}
                      </div>
                    )}
                  </>
                )}
```

- [ ] **Step 5: Update handleCreate to pass newBranch**

Change the `createInstance` call (around line 210):

```typescript
      const instance = await createInstance(
        selectedProject,
        selectedRepo,
        selectedToolType,
        displayName || undefined,
        cloneMode,
        isCreatingNewBranch ? baseBranch : branch,
        isCreatingNewBranch ? newBranchName : undefined
      );
```

- [ ] **Step 6: Reset new branch state on successful creation**

In the success handler (around line 227), add:

```typescript
      setIsCreatingNewBranch(false);
      setNewBranchName("");
      setBaseBranch("");
      setBranches([]);
```

- [ ] **Step 7: Commit**

```bash
git add apps/web/src/pages/sessions.tsx
git commit -m "feat: add branch dropdown and new branch creation UI

- Replace free-text branch input with dropdown of available branches
- Add 'Create new branch...' option with name and base branch inputs
- Load branches from API when repository is selected in clone mode
- Pass newBranch parameter to createInstance API"
```

---

### Task 5: Verify and Test

- [ ] **Step 1: Check TypeScript compilation**

```bash
cd apps/web && npm run typecheck
```

Expected: No errors

- [ ] **Step 2: Check Python syntax**

```bash
cd apps/api && python -m py_compile src/api/tool_instances.py
```

Expected: No errors

- [ ] **Step 3: Run backend tests if available**

```bash
cd apps/api && pytest src/tests/ -v -k "instance" || echo "No instance tests found"
```

- [ ] **Step 4: Commit**

```bash
git add -A
git commit -m "test: verify branch selection implementation compiles"
```

---

## Spec Coverage Check

| Spec Requirement | Task |
|---|---|
| Branch dropdown with available branches | Task 4 |
| "Create new branch..." option | Task 4 |
| New branch name input | Task 4 |
| Base branch dropdown | Task 4 |
| Backend clone + local branch creation | Task 2 |
| Frontend API integration | Task 1, 3 |

## Placeholder Scan

- No TBD, TODO, or "implement later" references
- All code is complete and copy-paste ready
- No vague instructions like "add appropriate error handling"

## Type Consistency Check

- `Branch` interface used consistently in Task 1 and Task 4
- `new_branch` / `newBranch` naming consistent between frontend and backend
- `CreateInstanceRequest` fields match API call in `sessions.ts`
