Worktrees and coding agents

Run the same project in several places at once (your checkout plus a review worktree, or several coding agents on one laptop) without sharing fed-managed runtime state. fed scopes containers, named volumes, and state to the directory. One command, fed isolate enable, also gives every declared host-port parameter a separate value.

Ports must be parameters

Isolation can only remap ports it knows about: the ones declared as type: port parameters and interpolated with {{...}}. A literal 4321 in a command or healthcheck is invisible to fed and will collide across workspaces. Write every port the fed way:

parameters:
  APP_PORT:
    type: port
    default: 4321
  DB_PORT:
    type: port
    default: 5433

services:
  database:
    image: postgres:16-alpine
    ports: ["{{DB_PORT}}:5432"]          # never "5433:5432"
    environment:
      POSTGRES_PASSWORD: postgres

  app:
    process: npm run dev -- --port {{APP_PORT}}
    startup_message: 'http://localhost:{{APP_PORT}}'
    environment:
      DATABASE_URL: 'postgres://localhost:{{DB_PORT}}/app'
    healthcheck:
      http_get: 'http://localhost:{{APP_PORT}}/health'

entrypoint: app

In normal (non-isolated) mode fed prefers the configured defaults; if one is occupied, it can allocate an alternative. Under isolation each workspace gets random ports from the outset, but only for ports declared this way. fed warns once at startup when it finds literal localhost:<port> references or literal host ports in the config.

Directory scoping

Everything fed tracks is scoped to the directory containing your fed.yaml:

Two directories are two independent stacks. There's nothing to configure, and it doesn't matter who created the directory: a worktree made by fed ws new, by hand, or by a coding agent behaves the same.

Isolation mode

Directory scoping keeps containers and state apart, but two checkouts of the same project still want the same default ports. Isolation mode fixes that. Run it once in the second checkout:

fed isolate enable       # Random ports + unique container names (persisted)
fed isolate status       # Show isolation state and port allocations
fed isolate rotate       # Re-roll ports and isolation ID
fed isolate disable      # Return to the directory's normal scope

fed isolate enable prints an isolation ID and the full port table, then persists both. Every fed command you run in that directory afterwards (start, scripts, tests, psql) uses the isolated ports and containers, across restarts, until you fed isolate disable.

CLI output calls the normal namespace the shared scope. It is shared by runs in this directory, not by different worktrees: normal container names, volumes, and state are still directory-scoped.

Quick isolation

fed start --isolate      # Enable isolation + start (one command)

On a new or stopped checkout, this has the same persistent result as fed isolate enable && fed start. Use the explicit command when services may already be running: it checks and offers to stop them before changing scopes.

What isolation does

Compose-backed services are already directory-scoped through a project name derived from the Compose file path. fed isolate enable does not replace that project name with the isolation ID, and fed isolate rotate does not create fresh Compose volumes.

Port management

fed ports list            # Show current allocations
fed ports list --json     # Machine-readable output

Allocations stay put until you fed isolate disable, re-roll them with fed isolate rotate, or wipe state with a full fed clean.

Git worktrees

Each worktree is a separate directory, so each worktree is a separate stack. Enable isolation right after creating one, before you run anything else:

# Main checkout: default ports
~/project $ fed start

# Second worktree: its own ports, containers, and volumes
~/project-review $ fed isolate enable
~/project-review $ fed start

Both stacks can run at the same time on the same machine when their host ports are declared as parameters. This scales past two. A docker ps on a busy laptop shows the main checkout on default ports and three isolated worktree stacks beside it, each under its own isolation ID.

Cookies and other shared namespaces

Ports isolate; localhost cookies don't. They're port-agnostic, so two stacks of the same app expire each other's sessions on every login. Scope the cookie name with the built-in FED_PROJECT_ID variable, which embeds the isolation ID, and each stack keeps its own session. The same variable namespaces anything else parallel stacks would share behind one hostname.

If you skip isolation, fed detects occupied defaults. An interactive start prompts for a response; a non-interactive start can fall back to alternative ports. Explicitly enabling isolation avoids that ambiguity and randomizes every declared port. Never use fed start --replace in a worktree: it can take the port by killing the other checkout's services.

Workspace management (beta)

fed ws (alias for fed workspace) automates git worktree creation and switching:

fed ws setup                # One-time: install shell integration (zsh or bash)
fed ws new my-feature -b    # Create branch + worktree, cd into it
fed ws list                 # Show all worktrees with service status
fed ws cd main              # Switch to another worktree
fed ws rm my-feature        # Stop services and remove worktree
fed ws prune                # Clean up worktrees for deleted branches

Worktrees are created as siblings to the repo in a <repo>-worktrees/ directory. Shell integration enables auto-cd. Without it, fed ws new and fed ws cd print the path instead.

Coding agents

When your coding-agent workflow uses one git worktree per task, each worktree is its own directory and can run its own full stack (database, backend, tests) without touching yours. One rule makes it predictable: isolate first.

Don't leave that to the agent's judgment. Put it in your AGENTS.md (or CLAUDE.md) so every agent starts every worktree the same way:

## Worktrees
Run `fed isolate enable` before any other fed command in a new worktree.
It persists: every fed command after it (start, tests, scripts) gets
the worktree's own ports, containers, and volumes.
Run `fed clean` before removing a worktree, so its containers and
volumes don't outlive it.

Why fed isolate enable rather than fed start --isolate? Agents rarely open with fed start. The first fed command is usually an install, a code generator, or a test run. fed isolate enable isolates the directory once, for whatever comes next; the flag only helps when the first command happens to be start.

The rule also protects the rest of your machine. An agent that skips it hits a port conflict on fed start. An agent trying to fix that on its own can reach for fed start --replace, which frees the port by killing another checkout's services. Agents follow written instructions more reliably than they recover from collisions.

No plugin or integration is needed. Isolation is directory-scoped, so it works the same whether the worktree came from Cursor, fed ws new, or git worktree add.

Isolated scripts

Scripts can opt into a throwaway child context with isolated: true (fresh ports, isolated direct image-backed containers and named volumes, and automatic cleanup). Compose limitations are covered in Scripts.

Cleaning up volumes

Isolation trades a little disk for safety: every isolated stack gets its own Docker named volumes (fed-<id>-…). Retire a stack without stopping it first (fed isolate rotate, a deleted worktree, an agent that moved on), and those volumes outlive it. Run a lot of parallel stacks and the leftovers add up to real disk.

Two commands keep it in check:

fed doctor     # among its checks: how many orphaned fed volumes you have
fed prune      # remove them: lists first, asks before deleting; --force to skip the prompt

fed prune only ever removes fed-managed volumes attached to no container, running or stopped, so it can't touch a live stack, or a stopped one you mean to resume. It shows you the list first; in a terminal it asks before deleting, and in a script or agent it deletes nothing without --force.

Most of the time you don't have to think about it: fed stop followed by fed clean removes a stack's declared volumes, and isolated: true scripts remove their throwaway volumes on exit. fed prune handles the rest: the stacks you rotated, abandoned, or deleted the worktree out from under.

Next: Scripts →