Skip to main content
← Back to list
01Issue
BugIn ProgressSwamp CLIPublic
Assigneesskunk-ape

Relationships

#2181 Worktree sessions (claude -w) burn turns on the git guard and write swamp state to a throwaway store

Opened by skunk-ape · 9/16/2026

Sessions started with claude -w <n> reliably burn several turns on worktree confusion, and silently write swamp lifecycle state into a store that is discarded with the worktree. Observed while running the issue-lifecycle skill to triage issue 1376.

1. Worktree git guard refuses any path containing ~

Claude Code 2.1.234 isolates -w sessions and guards commands that contain git. The static path resolver rejects a path outright if it contains ~, a ./.. segment, $(...)/${...}, or a glob:

function AOp(e){ ...; if(e.includes("~")) return !0; return !1 }
function RFn(e,t,r,n){ if(Qg(e)||e.includes("{}")||kOp(e)||AOp(e)||gx(e)||...) return null; ... }

So cd ~/sc/src/swamp && git log --oneline -3 is refused with "changes directory to a location computed at runtime before running git".

The guard is narrower than its wording suggests. Once a path resolves statically, the only destination check is whether it is the shared checkout the worktree branched from. Reading an unrelated sibling repo is allowed — the tilde was the entire problem. git -C /abs/path log --oneline -3 works first try, with no cd at all.

The agent did not infer this from the message. It retried with cd <abs> && ... prefixes on every subsequent call instead, and cd does not persist between Bash calls, so the prefix was re-paid each time.

Other forms that trip the same guard, for the record: wrapping git in xargs/env, assigning HOME/CDPATH/GIT_*, and spelling the command name as a glob.

2. A worktree gets a valid-looking swamp repo with an empty store

swamp-integration/.swamp.yaml is tracked, so it is checked out into every worktree. .swamp/ is gitignored — git ls-files shows zero tracked files under it — so the worktree gets a fresh, empty state store that looks initialized.

Two consequences, both silent:

  • The skill's existence check swamp data get issue-<N> state-main --json returns Model not found in a worktree even when the model exists in the main repo. The skill reads that as "new issue" and calls start, which unconditionally resets the phase to triaging. On a resumed issue this destroys classification, plan, and approvals. The check is unsound in a worktree.
  • start then wrote the lifecycle model to .claude/worktrees/<n>/swamp-integration/.swamp/auto-definitions/, a store that dies with the worktree, and warned that three pulled extensions had missing source files (also gitignored, also absent).

3. The skill's worktree note is wrong on two counts

references/triage.md says:

Add --repo-dir <path-to-main-repo> to all swamp commands, where the main repo is the parent of the .claude/worktrees/ directory.

The parent of .claude/worktrees/ is the git root, but the swamp repo is swamp-integration/ — per AGENTS.md, all swamp work runs from there. And --repo-dir is a per-subcommand flag that the agent dropped on the very next command in the trace; the durable form is the SWAMP_REPO_DIR env var.

4. Collateral: a stray swamp init at the git root

/.swamp.yaml carries initializedAt: "2026-09-16T16:02:03Z" with tools: [claude], three minutes before the traced start at 16:05:07, and the tracked root CLAUDE.md has an uncommitted 45-line swamp managed block. A second swamp repo was initialized at the swamp-club git root, against AGENTS.md. Not conclusively attributed to the traced session, but same time window and same root cause: swamp could not resolve a repo from where the agent was standing.

5. Two divergent copies of the skill

.claude/skills/issue-lifecycle/ and swamp-integration/.claude/skills/issue-lifecycle/ differ in SKILL.md, references/implementation.md, and references/triage.md. Which one loads depends on where the session started.

Proposed fixes

  1. AGENTS.md — one line: in a claude -w session, spell every path in a git-bearing command as a literal absolute path (no ~, no ./.., no $(...), no globs), and read a sibling repo with git -C /abs/path, never cd.
  2. .claude/settings.json — set env.SWAMP_REPO_DIR to the absolute swamp-integration path, which removes the whole class. Caveat: infrastructure/ has its own .swamp.yaml and would be overridden too; if that repo is live, correct the skill note instead.
  3. references/triage.md — name swamp-integration explicitly, use SWAMP_REPO_DIR, and state that Model not found is untrustworthy until the repo dir is pinned, so start is never called on a stale negative.
  4. Reconcile or symlink the two skill copies.
  5. Decide whether the root swamp init was intended; revert CLAUDE.md, /.swamp.yaml, and /.swamp/ if not.
02Bog Flow
OPENTRIAGEDIN PROGRESSSHIPPED+ 2 MORETRIAGE+ 5 MOREREVIEW+ 4 MOREPR_LINKEDSHIPPED

In Progress

9/16/2026, 6:21:01 PM

Click a lifecycle step above to view its details.

03Sludge Pulse
skunk-ape assigned skunk-ape9/16/2026, 4:32:20 PM

Sign in to post a ripple.