Workspace Pattern - Git Worktrees for Multi-Repo Sessions

⚠️ DEPRECATED: This document describes the nested worktree pattern which doesn’t work due to gitignore conflicts.
See: workspace-pattern-v2.md for the correct sibling worktree pattern.

Date: 2026-01-30
Context: How OpenCode sessions see multiple repositories


The Problem

OpenCode sessions are scoped to a single git repository. When working with multiple repos in workspace-root/repos/, OpenCode can’t see changes because:

  1. repos/ is gitignored in workspace-root
  2. Each repo in repos/ is an independent git repository
  3. OpenCode running at workspace-root level only sees workspace-root changes

The Solution: Git Worktrees

Use git worktrees to create isolated workspaces where OpenCode can see all necessary repositories.

Pattern Overview

workspace-root/                    # Main git repository
├── repos/                         # Gitignored - independent repos
│   ├── ai-dev/                    # Standalone git repo
│   ├── domain-apis/               # Standalone git repo
│   └── k8s-lab/                   # Standalone git repo
│
└── .builders/                     # Builder workspaces
    ├── ai-dev-gateway-slack/      # Git worktree of workspace-root
    │   ├── .git → worktree ref    # Points to workspace-root git
    │   ├── repos/                 # Worktrees of actual repos
    │   │   └── ai-dev/            # Worktree → repos/ai-dev
    │   ├── Taskfile.yaml          # From workspace-root
    │   └── workspaces.yaml        # From workspace-root
    │   └── [OpenCode runs here]   # ← Session sees everything!
    │
    └── ai-dev-gateway-backend/    # Parallel session, isolated worktree
        ├── .git → worktree ref
        ├── repos/
        │   └── ai-dev/            # Separate worktree → repos/ai-dev
        └── [OpenCode runs here]   # ← Different session, same repo

How It Works

Step 1: Create Builder Workspace (Worktree)

# Create a git worktree of workspace-root
git worktree add .builders/ai-dev-gateway-slack -b builder/ai-dev-gateway-slack
 
# Result:
# - .builders/ai-dev-gateway-slack/ created
# - New branch: builder/ai-dev-gateway-slack
# - Workspace-root files are visible here

What this gives you:

  • ✅ Isolated workspace
  • ✅ All workspace-root files visible
  • ✅ Changes tracked in git (separate branch)
  • ✅ No conflicts with other worktrees

Step 2: Initialize Repositories Inside Builder

cd .builders/ai-dev-gateway-slack
task builder:init REPOS=ai-dev,domain-apis,k8s-lab

What builder:init does:

  1. Creates repos/ directory in builder workspace
  2. For each repo, creates a git worktree:
    git worktree add repos/ai-dev ../../repos/ai-dev/main
  3. Copies configuration files (Taskfile.yaml, workspaces.yaml)

Result:

.builders/ai-dev-gateway-slack/
├── repos/
│   ├── ai-dev/         # Worktree → workspace-root/repos/ai-dev
│   ├── domain-apis/    # Worktree → workspace-root/repos/domain-apis
│   └── k8s-lab/        # Worktree → workspace-root/repos/k8s-lab
├── Taskfile.yaml
└── workspaces.yaml

Step 3: Start OpenCode in Builder Directory

opencode serve --session ai-dev-gateway-slack --project .

OpenCode now sees:

  • ✅ All workspace-root files (via builder worktree)
  • ✅ All repo files in repos/ (via repo worktrees)
  • ✅ All changes are git-tracked
  • ✅ Clean separation from other sessions

Parallel Sessions

Multiple OpenCode sessions can work on the same repository simultaneously by using different worktrees:

# Session 1: Slack team
git worktree add .builders/ai-dev-gateway-slack -b builder/slack
cd .builders/ai-dev-gateway-slack
task builder:init REPOS=ai-dev
opencode serve --session slack
 
# Session 2: Backend team
git worktree add .builders/ai-dev-gateway-backend -b builder/backend
cd .builders/ai-dev-gateway-backend
task builder:init REPOS=ai-dev
opencode serve --session backend
 
# Both work on ai-dev repo, but in isolated worktrees
# Changes don't conflict until merged

Isolation:

  • Each session has its own git branch
  • Each session has its own worktree of repos
  • No file conflicts during development
  • Integrate via git merge when ready

Integration with AI Dev Gateway

When a user runs /opencode start in Slack, the gateway:

1. Create Builder Workspace

async def create_builder_workspace(category: str, project: str):
    builder_name = f"{category}-{project}"
    branch_name = f"builder/{builder_name}"
    
    # Create worktree of workspace-root
    await run_command(
        f"git worktree add .builders/{builder_name} -b {branch_name}"
    )
    
    return f".builders/{builder_name}"

2. Initialize Repositories

async def initialize_repos(builder_path: str, repos: List[str]):
    repos_arg = ",".join(repos)
    
    # Run builder:init task
    await run_command(
        f"task builder:init REPOS={repos_arg}",
        cwd=builder_path
    )

3. Start OpenCode Session

async def start_opencode_session(builder_path: str, session_id: str):
    # Start OpenCode in builder directory
    await run_command(
        f"opencode serve --session {session_id} --project .",
        cwd=builder_path
    )

Benefits

For Development

  1. Visibility: OpenCode sees all necessary files
  2. Isolation: Each session is independent
  3. Parallelism: Multiple sessions on same repo without conflicts
  4. Git-tracked: All changes tracked properly

For the Gateway

  1. Clean workspaces: Each project gets isolated environment
  2. Easy cleanup: Delete worktree when done
  3. State persistence: Git branches preserve work
  4. Testing isolation: Each deployment can have its own workspace

Cleanup

When a session is complete:

# Delete the worktree
git worktree remove .builders/ai-dev-gateway-slack
 
# Optionally delete the branch
git branch -d builder/ai-dev-gateway-slack

Gateway automation:

async def cleanup_builder(builder_name: str, keep_branch: bool = False):
    # Remove worktree
    await run_command(f"git worktree remove .builders/{builder_name}")
    
    # Optionally remove branch
    if not keep_branch:
        await run_command(f"git branch -D builder/{builder_name}")

Example: Complete Session Lifecycle

# User: /opencode start
# Form: category=ai-dev, project=gateway-slack, repos=ai-dev
 
# Gateway Step 1: Create builder workspace
$ git worktree add .builders/ai-dev-gateway-slack -b builder/ai-dev-gateway-slack
Preparing worktree (new branch 'builder/ai-dev-gateway-slack')
HEAD is now at 0756050 fix: Correct BMAD slash commands
 
# Gateway Step 2: Initialize repos
$ cd .builders/ai-dev-gateway-slack
$ task builder:init REPOS=ai-dev
📥 Cloning ai-dev...
 Workspace initialization complete!
 
# Gateway Step 3: Start OpenCode
$ opencode serve --session ai-dev-gateway-slack --project .
OpenCode server started on port 8080
 
# Developer works...
# Files changed in:
# - .builders/ai-dev-gateway-slack/repos/ai-dev/services/gateway/...
 
# When complete:
$ git worktree remove .builders/ai-dev-gateway-slack
$ git branch -d builder/ai-dev-gateway-slack

Verification

To verify a builder workspace is set up correctly:

# 1. Check it's a worktree
$ cat .builders/ai-dev-gateway-slack/.git
gitdir: /Users/craig/src/workspace-root/.git/worktrees/ai-dev-gateway-slack
 
# 2. Check branch
$ cd .builders/ai-dev-gateway-slack
$ git branch
* builder/ai-dev-gateway-slack
  main
 
# 3. Check repos are worktrees
$ cat repos/ai-dev/.git
gitdir: /Users/craig/src/workspace-root/repos/ai-dev/.git/worktrees/...
 
# 4. Verify OpenCode sees changes
$ opencode session list
# Should show session with visible repo files

Common Issues

Issue: “fatal: invalid reference”

Cause: Branch name conflicts

Solution:

# Use unique branch names per session
git worktree add .builders/session-123 -b builder/session-123

Issue: “OpenCode doesn’t see repo files”

Cause: OpenCode not started in builder directory

Solution:

# Always CD into builder directory first
cd .builders/ai-dev-gateway-slack
opencode serve --session <id>

Issue: “Worktree already exists”

Cause: Previous session not cleaned up

Solution:

# Remove old worktree
git worktree remove .builders/old-session
git worktree prune

References


Summary

The Pattern:

  1. Create builder workspace (worktree of workspace-root)
  2. Initialize repos inside builder (worktrees of actual repos)
  3. Start OpenCode in builder directory
  4. OpenCode sees everything!

Why it works:

  • Worktrees are part of git tree (not gitignored)
  • OpenCode scans git tree for changes
  • All repositories visible in single workspace
  • Clean isolation between parallel sessions

This pattern enables the multi-repo, multi-session development model that powers the AI Dev Gateway.