# How OpenWork's `pnpm dev:worktree` Enables Git Worktree-Based Local Development

> Learn how OpenWork's pnpm dev:worktree command leverages Git worktrees for isolated Electron dev instances. Achieve unique user-data directories, debug ports, and mock keychain isolation from one repo.

- Repository: [Different AI/openwork](https://github.com/different-ai/openwork)
- Tags: how-to-guide
- Published: 2026-08-01

---

**OpenWork's `pnpm dev:worktree` command uses Git worktrees with environment variables to spawn isolated Electron development instances from a single repository checkout, each with unique user-data directories, auto-allocated debug ports, and mock keychain isolation.**

OpenWork's local development workflow centers on `pnpm dev:worktree`, a script that enables developers to run multiple independent desktop app instances without separate repository clones. This approach leverages Git worktrees to keep branches isolated while sharing a single `.git` history, then uses environment-driven profile isolation to prevent Electron state conflicts between parallel runs.

## What `pnpm dev:worktree` Actually Does

The script is defined in the root [`package.json`](https://github.com/different-ai/openwork/blob/main/package.json) as follows:

```json
"dev:worktree": "OPENWORK_DEV_MODE=1 OPENWORK_DEV_PROFILE=auto OPENWORK_ELECTRON_USE_MOCK_KEYCHAIN=${OPENWORK_ELECTRON_USE_MOCK_KEYCHAIN:-1} OPENWORK_ELECTRON_REMOTE_DEBUG_PORT=0 PORT=0 pnpm --filter @openwork/desktop dev"

```

This single command sets up four key behaviors:

- **`OPENWORK_DEV_MODE=1`** — Signals the Electron app to run in development build mode
- **`OPENWORK_DEV_PROFILE=auto`** — Triggers automatic profile name generation based on worktree path (implemented in `apps/desktop/electron/dev-profile.mjs`)
- **`OPENWORK_ELECTRON_USE_MOCK_KEYCHAIN=1`** — Prevents macOS keychain dialogs during development
- **`OPENWORK_ELECTRON_REMOTE_DEBUG_PORT=0` & `PORT=0`** — Requests dynamic port allocation for Chrome DevTools Protocol and Vite dev-server

## How Profile Isolation Works

### Automatic Profile Name Generation

In `apps/desktop/electron/dev-profile.mjs`, the `deriveAutoDevProfileName` function creates a stable profile identifier from the worktree's filesystem path. This ensures that returning to the same worktree directory always yields the same profile, while different worktrees get distinct profiles.

The `resolveUserDataPath` function then constructs an isolated directory:

```

~/.config/openwork/<profile-name>/

```

This directory houses Electron's storage, cookies, cache, and keychain state—completely separating parallel development instances.

### Port Allocation for Debugging

Both debug and dev-server ports use zero-value placeholders that trigger probing logic in `apps/desktop/electron/main.mjs`. The `findFreeCdpPort` function (lines 215-236) scans from `9223` upward to locate an available port, then prints the result in the startup banner:

```

[openwork] dev profile=/home/user/.config/openwork/feature-xyz cdp=http://127.0.0.1:9224

```

## Running Multiple Worktree Instances

### Basic Worktree Setup

Create and enter a new worktree, then launch an isolated dev instance:

```bash

# Create worktree for a feature branch

git worktree add -b feature-xyz ../openwork-feature-xyz feature-xyz

cd ../openwork-feature-xyz

# Launch with auto-generated profile

pnpm dev:worktree

```

### Custom Profile Names for Scripts

Override the automatic naming when you need deterministic paths for automation:

```bash
OPENWORK_DEV_PROFILE=my-demo pnpm dev:worktree

```

This creates `~/.config/openwork/my-demo/`, making the profile easy to reference from CI pipelines or test scripts.

## Preventing Conflicts Between Instances

The implementation includes safeguards against common development friction:

- **Mock keychain on macOS**: When `OPENWORK_ELECTRON_USE_MOCK_KEYCHAIN=1`, the startup code in `main.mjs` (around line 92) appends `--use-mock-keychain` to Electron's command line, eliminating the "Login" keychain modal that would otherwise block headless or automated runs.

- **Single-instance enforcement**: If a second process attempts to use an already-active profile, the app detects the lock file and exits cleanly with an explanatory message. This prevents the confusing scenario where a CDP port remains open but no window is visible.

- **Parallel debugging**: Each instance exposes its own CDP endpoint, enabling simultaneous Chrome DevTools or OpenCode agent connections to multiple running apps.

## Attaching Debug Tools to a Worktree Instance

After `pnpm dev:worktree` starts, use the printed CDP URL to connect automation tools:

```bash

# Example: connect OpenCode agent to the running instance

pnpm exec -- openwork-agent --connect http://127.0.0.1:9224

```

This integration supports the **fraimz** proof workflow, where agents drive the Electron UI, execute automation tasks, and capture screenshots without interfering with other active development sessions.

## Key Source Files

| File | Purpose |
|------|---------|
| [`package.json`](https://github.com/different-ai/openwork/blob/main/package.json) | Defines the `dev:worktree` script with environment variable preset |
| `apps/desktop/electron/dev-profile.mjs` | Implements `deriveAutoDevProfileName` and `resolveUserDataPath` for stable profile derivation |
| `apps/desktop/electron/main.mjs` | Handles port probing (`findFreeCdpPort`, lines 215-236), mock keychain flags (line 92), and startup banner output (line 247) |

## Summary

- **`pnpm dev:worktree`** combines Git worktrees with environment variables to enable parallel, isolated Electron development from one repository
- **Auto-generated profiles** create stable, path-based user-data directories in `~/.config/openwork/`
- **Dynamic port allocation** ensures each instance gets unique CDP and Vite ports without manual configuration
- **Mock keychain and lock-file detection** prevent macOS dialogs and duplicate-instance crashes
- **Custom profile names** via `OPENWORK_DEV_PROFILE` support deterministic paths for CI and automation

## Frequently Asked Questions

### What is a Git worktree and why does OpenWork use it?

A Git worktree is a linked working directory that shares the same repository history but checks out a different branch. OpenWork uses worktrees so developers can keep multiple feature branches open simultaneously—each with its own filesystem state—without cloning the repository multiple times or constantly switching branches.

### How does OpenWork prevent worktree instances from interfering with each other?

Each `pnpm dev:worktree` invocation generates or uses a unique profile name that determines Electron's user-data directory. Stored state—including cookies, local storage, and application cache—is written to `~/.config/openwork/<profile>/`, ensuring complete isolation. Additionally, lock files prevent the same profile from running twice.

### Can I use `pnpm dev:worktree` without creating an actual Git worktree?

Yes. The script functions in any directory; `OPENWORK_DEV_PROFILE=auto` derives a profile from the current path. However, the workflow is designed for worktrees—using it in the main repository checkout produces a profile based on that path, which may not provide meaningful isolation if you're not using separate directories.

### Why does the mock keychain default to enabled in worktree mode?

macOS keychain access triggers a system modal dialog that blocks unattended or automated development sessions. Defaulting `OPENWORK_ELECTRON_USE_MOCK_KEYCHAIN` to `1` ensures `pnpm dev:worktree` runs headlessly-friendly without manual intervention, critical for CI pipelines and agent-driven workflows like fraimz proof generation.