From ebf2b8ff7904bf4aca65ee21e31cd639ce2eff79 Mon Sep 17 00:00:00 2001 From: Manav Shrivastava Date: Mon, 10 Aug 2026 03:59:44 +0530 Subject: [PATCH] fix(server): persist worktree runtime port when ambient PORT does not match (#1849) (#1930) ## What was done Replaced the strict `!nonEmpty(process.env.PORT)` guard in `maybePersistWorktreeRuntimePorts` with a new `isPortPinnedByRuntimeEnv` helper function. This function checks if `process.env.PORT` is set, but only suppresses persisting the port to configuration if the ambient `PORT` matches the newly allocated `selectedPort`. ## Why it matters Fixes issue #1849. Previously, if an ambient `PORT` environment variable was exported globally (like inheriting from the shell running the parent workspace), worktrees would silently fail to write their collision-avoiding ports (e.g. 3103 instead of 3100) back to their respective local `config.json` files. This resulted in orphaned sub-worktrees and lost port tracking on reboot. With this fix, worktrees correctly persist their assigned ports even while nested under an inherited environment variables stack, while continuing to respect manual, explicit pinning. ## How to verify 1. Export a port in the shell explicitly: `export PORT=3100`. 2. Launch a sub-worktree instance which receives an auto-assigned free port (e.g., `3103`). 3. View the underlying `config.json` for that worktree inside `.paperclip/worktrees/`. 4. The config file should correctly contain `{"server": {"port": 3103}}` rather than dropping the write operation. ## Risks None expected. The `Number()` and `Number.isInteger()` checks handle parsing edge cases cleanly, defaulting robustly to preventing writes if `process.env.PORT` is somehow malformed (e.g., set to a non-integer), ensuring absolute safety during misconfigurations. Co-authored-by: manavshrivastavagit --- server/src/worktree-config.ts | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/server/src/worktree-config.ts b/server/src/worktree-config.ts index 913451d0be..b05686204e 100644 --- a/server/src/worktree-config.ts +++ b/server/src/worktree-config.ts @@ -650,6 +650,14 @@ export function maybeRepairLegacyWorktreeConfigAndEnvFiles(): { return { repairedConfig, repairedEnv }; } +function isPortPinnedByRuntimeEnv(rawValue: string | null | undefined, selectedPort: number): boolean { + const normalized = nonEmpty(rawValue); + if (!normalized) return false; + const parsedPort = Number(normalized); + if (!Number.isInteger(parsedPort) || parsedPort <= 0) return true; + return parsedPort === selectedPort; +} + export function maybePersistWorktreeRuntimePorts(input: { serverPort: number; databasePort?: number | null; @@ -667,7 +675,7 @@ export function maybePersistWorktreeRuntimePorts(input: { const { config, changed } = applyRuntimePortSelectionToConfig(fileConfig, { serverPort: input.serverPort, databasePort: input.databasePort, - allowServerPortWrite: !nonEmpty(process.env.PORT), + allowServerPortWrite: !isPortPinnedByRuntimeEnv(process.env.PORT, input.serverPort), allowDatabasePortWrite: !nonEmpty(process.env.DATABASE_URL), }); @@ -675,3 +683,4 @@ export function maybePersistWorktreeRuntimePorts(input: { writeConfigFile(context.configPath, config); } } +