Skip to content

Sessions & worktrees

Every agent session in a git project works on its own branch, in its own checkout of your repo. Run five at once and none of them touches another’s files, or yours. Your project folder stays on whatever branch you had checked out, and each session’s work sits on a normal git branch until you bring it back.

When you start a session in a git repo, spwn:

  1. creates a branch named spwn/<id>, where <id> is the first eight characters of the session’s id, starting from the branch your project folder has checked out (its base);
  2. checks that branch out into a worktree of its own, with git worktree add;
  3. clones your heavy build folders into it, so the agent can build straight away;
  4. runs the agent in that worktree.

The branch lives in your real repo. git branch lists it, and you can check it out, diff it or merge it like any other branch:

Terminal window
git log main..spwn/3f9c2a1b # what the session did
git merge spwn/3f9c2a1b # bring it in by hand

The strip at the top of a session shows where its code lives, its branch and the base it merges back into, how many commits it is ahead, and whether it has uncommitted changes. Click the strip to open the Inspector. Its Overview tab repeats the same facts and lists the changed files.

Switching between sessions only changes what you’re looking at. Nothing on disk moves, so a session working in the background keeps going untouched.

Settings → Sessions → Session worktree location decides where new worktrees are created. The setting is worktreeLocation:

Value Where a session’s worktree goes Notes
sibling (default) <repo-parent>/.<repo-name>-worktrees/<session> Outside your working tree, so builds, file watchers and IDE indexers never see it.
internal <repo>/.spwn/worktrees/<session> spwn adds /.spwn/worktrees/ to .git/info/exclude, so git ignores it without a change to your .gitignore. Tools with explicit include globs may still pick it up.
appData <app data>/worktrees/<session> Away from your repos entirely. The app data folder is ~/Library/Application Support/gg.spwn.spwn on macOS and ~/.local/share/gg.spwn.spwn on Linux.

For a repo at ~/code/shop, the default puts session worktrees in ~/code/.shop-worktrees/.

The setting applies to sessions started after you change it. Existing worktrees stay where they were made.

A fresh worktree only has your tracked files, so there would be no node_modules and no target, and the agent’s first build would be a cold install. spwn copies these gitignored folders from your project folder into the new worktree, when they exist:

node_modules target .venv venv dist build .next .svelte-kit .turbo

On APFS (macOS) the copy is a copy-on-write clone (cp -c). It’s near-instant and takes almost no disk until the agent changes something. Elsewhere it’s a plain recursive copy.

After each finished turn, spwn stages everything in the worktree (git add -A, which respects .gitignore) and commits it on the session branch:

spwn turn 7e1d0c44

The commit is made as spwn session <spwn@localhost> with --no-verify, so it works in a repo with no configured identity and an unattended run can’t trip a pre-commit hook. A turn that changed nothing makes no commit.

The session branch always carries real history. There is nothing to reconstruct when you merge, and a fork from turn 3 can start from turn 3’s code.

Parallel sessions start from the same base. When the base moves they tend to hit the same conflict, once each. spwn turns on git’s rerere (“reuse recorded resolution”) when it creates a worktree, so a conflict resolved in one session is replayed automatically in the others. rerere’s cache lives in the repo’s shared git directory, so every worktree sees it.

spwn only sets rerere.enabled true when you haven’t set it yourself. An explicit rerere.enabled false is left alone.

Worktree creation, the per-turn commit, checkpoints and worktree removal aren’t hardcoded. They ship as default scripts in ~/.spwn/hooks/, and you can read them:

Script What it does
session-created.d/10-worktree.sh Creates the branch and worktree, turns on rerere, clones heavy folders.
session-turn.d/10-commit.sh Commits each turn.
session-turn.d/20-checkpoint.sh Takes a file snapshot each turn.
session-deleted.d/90-worktree.sh Removes the worktree and deletes the branch.

To add a step of your own, drop a separate script into the same folder, such as session-created.d/50-my-setup.sh. Don’t edit spwn’s own scripts, because spwn may overwrite them when it updates. Delete one to opt out of what it does. With 10-commit.sh gone, turns stop auto-committing. See How hooks work.

Every turn also takes a file snapshot of the worktree. It’s a copy-on-write clone on APFS, so a snapshot costs nearly nothing until files change. Together with the agent’s own conversation history, snapshots let you roll a session back.

Open the Inspector’s Transcript tab (the Transcript button on the agent bar) and hover any assistant message. ↺ Return here offers two choices:

  • Conversation only rolls the conversation back to this point. Your next message continues from here and the later turns drop. The files stay as they are.
  • Conversation + restore files also puts the worktree’s files back to their snapshot from this turn. It’s greyed out when that turn has no snapshot.

spwn rewinds by driving the agent’s own /rewind menu. Before it confirms, it reads back the highlighted row and checks that it matches the turn you picked. If the row doesn’t match, spwn stops and tells you, and nothing has changed.

The Inspector’s Timeline tab lists the file snapshots on their own, newest first:

  • Restore puts the files back to one snapshot without touching the conversation.
  • ⟲ Undo last change restores the snapshot from the turn before the latest one, so it undoes the last turn’s edits.

Every file restore does the same things:

  • It saves a safety snapshot first, so the restore itself can be undone from the same list.
  • It rewrites working files and deletes files created since the snapshot.
  • It never touches .git, so commit history is kept. It also leaves the heavy folders (node_modules, target and the rest) alone.

A restore waits for the current turn to finish. spwn keeps the newest 20 turn snapshots and the newest 6 safety snapshots per session, and prunes older ones.

Delete a session with the × on its sidebar row. spwn removes:

  • the worktree, with git worktree remove --force, so uncommitted changes don’t block it;
  • the spwn/<id> branch, with git branch -D;
  • the session’s file snapshots;
  • its entry in the land order, if it had one.

Before any of that, your repo’s own session-deleted hooks run inside the worktree, so teardown can happen while the files still exist.

Deleting a branch with unmerged work loses that work. The confirm dialog checks first and names exactly what’s at risk:

  • Unmerged commits: for example, 3 not in "main";
  • Uncommitted changes: yes.

When there’s something to lose, the dialog also offers Open to merge first, which takes you to the session so you can bring the work back before you delete it.

If a project folder isn’t a git repository, sessions run directly in the project folder. They get no branch, no worktree, no per-turn commit, no file snapshots and nothing to merge, and the status strip says no worktree. Several sessions in the same non-git folder all edit the same files.

The same happens when your project folder is on a detached HEAD: with no branch checked out there’s no base to start from.

To get branches and merging, run git init in the project and start a new session.