Skip to content

Fix dev server port conflicts across worktrees#312

Merged
Eliezir merged 4 commits intomainfrom
eliezir/dev-port-conflicts
Apr 20, 2026
Merged

Fix dev server port conflicts across worktrees#312
Eliezir merged 4 commits intomainfrom
eliezir/dev-port-conflicts

Conversation

@Eliezir
Copy link
Copy Markdown
Collaborator

@Eliezir Eliezir commented Apr 20, 2026

Closes #290.

Summary

Fixes dev server port conflicts when running multiple worktrees in parallel (Conductor or plain git worktree). Before: only the first worktree's API binds :3001; other studios silently proxy to the stale first instance, so endpoints that only exist on the current branch return 404 in what looks like a UI bug. After: each worktree picks its own free API port, and its studio proxy follows.

As a bonus for parallel-worktree workflows, the browser tab title now reflects the workspace (Conductor workspace name, or current git branch as a fallback), so tabs are trivially distinguishable at a glance.

Previous Conductor workspace Branch fallback
ADT Studio ADT Studio — Cool workspace ADT Studio — Dev port conflicts

How

  • scripts/dev.mjs — root dev orchestrator. Finds a free port (sequential scan from 3001), then spawns the existing pnpm --parallel dev with PORT and API_PROXY_TARGET threaded in as env vars. Also detects a workspace label (from CONDUCTOR_WORKSPACE_NAME, falling back to the current git branch's leaf segment) and forwards it as VITE_WORKSPACE_NAME.
  • scripts/find-free-port.mjs — pure-Node port probe. Logic adapted from Zig's findFreePort in the zig/electron branch (apps/electron/src/main/api/port.ts).
  • apps/studio/vite.config.ts — proxy target now reads API_PROXY_TARGET with http://localhost:3001 fallback.
  • apps/studio/src/main.tsx — when VITE_WORKSPACE_NAME is set at dev time, appends it to document.title. Unset in production builds, so end users see the default title.
  • apps/studio/src/vite-env.d.ts — type declaration for the new env var.
  • package.json — root dev script now calls node scripts/dev.mjs. dev:api / dev:studio unchanged.

Approach

We went with a monorepo-level orchestrator because it's self-contained, tool-agnostic (works with plain git worktree, Conductor, multiple terminals), needs zero per-worktree config, and adds no dependencies. A more robust alternative would be a dedicated tool like Portless, which handles automatic subdomain routing with zero env wiring — at the cost of another dev dependency and per-developer setup.

Scope

Web dev only. The electron launch path already picks its own port via its own supervisor logic and is untouched. Production builds are also untouched: the api's PORT fallback stays 3001, vite build ignores server.proxy, and VITE_WORKSPACE_NAME is unset so the tab title is unchanged.

Test plan

  • Run pnpm dev in one worktree → [dev] API port: 3001, studio on 5173
  • Run pnpm dev in a second worktree concurrently → [dev] API port: 3002, studio on 5174, /api/* requests from the second studio hit the second worktree's API
  • Temporary console.log on an api route in worktree 2, triggered from worktree 2's studio, appears in worktree 2's terminal (not worktree 1's)
  • Under Conductor: each tab title reads ADT Studio — <workspace name>
  • Outside Conductor (plain worktree): tab title reads ADT Studio — <branch leaf> (e.g. Dev port conflicts for Eliezir/dev-port-conflicts)
  • Solo pnpm dev:studio still proxies to :3001 and shows the plain ADT Studio title (backwards compatible)
  • pnpm typecheck passes

Eliezir added 4 commits April 18, 2026 15:20
Running multiple worktrees in parallel (Conductor or plain git worktree)
caused studios on 5174/5175 to silently proxy to the first worktree's
API on :3001, serving stale code from the wrong branch.

A root orchestrator (scripts/dev.mjs) now picks a free port and threads
PORT + API_PROXY_TARGET into the api/studio children via env vars. The
vite proxy reads API_PROXY_TARGET with a :3001 fallback so solo dev and
prod builds are unaffected. Port-probe helper adapted from the
findFreePort in zig/electron.
- Added support for setting the document title based on the VITE_WORKSPACE_NAME environment variable in main.tsx.
- Updated vite-env.d.ts to define the VITE_WORKSPACE_NAME variable for better type safety.
- Enhanced dev.mjs to detect and log the workspace name from the conductor environment or current git branch, improving clarity during development.
@Eliezir
Copy link
Copy Markdown
Collaborator Author

Eliezir commented Apr 20, 2026

Continuation of #295, which was auto-closed by GitHub when the head branch was renamed from Eliezir/dev-port-conflicts to eliezir/dev-port-conflicts to match the project's existing branch-naming convention. Same commits, same description.

@Eliezir Eliezir self-assigned this Apr 20, 2026
@Eliezir Eliezir merged commit 0bc4425 into main Apr 20, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Dev server port conflicts across multiple worktrees (Conductor) — studios proxy to the wrong API

1 participant