Skip to content

Feature worktrees & isolated environments

Develop each feature in its own git worktree with a fully isolated runtime — its own postgres database, its own ports, its own .env — all derived from the branch name. One shared platform-db container hosts one database per branch; pg-boss creates its pgboss schema inside that database, so queue isolation comes for free.

Two reusable scripts cover the whole lifecycle:

CommandWhat it does
bun run worktree createCreate the worktree (branch + port offset + bun install)
bun run env:featureProvision the isolated env (branch DB + .env files + migrate)
bun run worktree listList all worktrees
bun run env:feature:downDrop the branch database
bun run worktree removeRemove the worktree
Terminal window
bun run worktree create feat/my-feature # auto-assigns next free port offset
bun run worktree create feat/my-feature 100 # pin a port offset

Creates ../platform-worktrees/feat/my-feature, assigns the next free port offset, runs bun install, and opens Warp tabs. Flags: --no-open, --no-install.

Run this inside the new worktree:

Terminal window
cd ../platform-worktrees/feat/my-feature
bun run env:feature

This derives a slug + port offset from the branch name, seeds .env, apps/api/.env, apps/dashboard/.env, and apps/developer-website/.env from the main worktree, rewrites them to point at an isolated postgres database (platform_<slug>) and offset ports (API 3000+offset, Dashboard 5000+offset, Dev website 5003+offset), then runs migrations. It is idempotent — re-running reuses the existing offset/DB and only fills gaps.

Then develop as usual:

Terminal window
bun run dev # API + dashboard
bun run developer-website # docs/marketing site (developer.viite.ai), offset port
Terminal window
bun run env:feature:down # drop the branch database
bun run env:feature:down --purge # ...and remove the worktree .env files
bun run worktree remove feat/my-feature # remove the worktree
bun run worktree remove feat/my-feature --delete-branch --force
  • Dual-React invalid-hook error. If the dashboard fails in a worktree (tailwind-resolve, then useMemo of null / “invalid hook call”), remove the symlinked apps/dashboard/node_modules and run bun install at the worktree root. Vite aliases platform-ui to source, so any split React copy breaks hooks.
  • Vite doesn’t see .env. Bun doesn’t inject .env into a spawned Vite, so always use env:feature (which handles the loadEnv wiring) rather than hand-rolling .env files. The same applies to the Astro dev server (apps/developer-website): its astro.config.mjs reads DEVELOPER_WEBSITE_PORT via loadEnv from its own dir, so the port offset only binds when the apps/developer-website/.env written by env:feature is present.