Skip to content

fix(skills): worktree-isolated agents cannot invoke a skill whose pre-compute block contains a $-expansion (21 skills; NOT git — see correction comment) #1619

Description

@kyle-sexton

Summary

A worktree-isolated agent (Agent with isolation: "worktree", or any subagent with a pinned cwd)
cannot invoke any skill that declares two or more ! precompute commands when at least one is a
git command.
The invocation fails before the skill body is ever reached.

Discovered while running /session-flow:handoff at the end of an inbox item. It is not a
handoff-specific bug — handoff is just where it surfaced. 42 skills across this marketplace
match the pattern
, including most of the core workflow: source-control:commit,
source-control:pull-request, source-control:worktree, implementation:implement,
verification:confirm, review:fanout, session-flow:retro, session-flow:handoff,
planning:plan, testing:plan, toolchain:check.

Reproduction

From an agent isolated in .claude/worktrees/<name>:

Skill(session-flow:handoff)

fails with:

This agent is isolated in the worktree C:\...\.claude\worktrees\agent-a4324d31193dece2f, but this
command is too complex to verify that it stays inside the worktree; break it into plain, separate
commands. Refusing to run it — a worktree-isolated agent's git operations must target its own
worktree.

Identical failure with and without arguments. The skill body never runs.

Root cause

plugins/session-flow/skills/handoff/SKILL.md declares four precompute lines:

Current branch: !`git branch --show-current 2>/dev/null || echo "unknown"`
Claude session: !`echo "${CLAUDE_CODE_SESSION_ID:-unknown}" || echo "unknown"`
Uncommitted changes: !`git status --porcelain 2>/dev/null | head -20 || echo "clean"`
Recent commits: !`git log --oneline -5 2>/dev/null || echo "no commits"`

Each line passes the guard individually — verified one at a time:

precompute line result
git branch --show-current 2>/dev/null || echo "unknown" ok — main
git status --porcelain 2>/dev/null | head -20 || echo "clean" ok — (clean)
git log --oneline -5 2>/dev/null || echo "no commits" ok — 5 commits

Joined with ; into a single invocation, the exact guard error reproduces:

git branch --show-current 2>/dev/null || echo "unknown"; echo "${CLAUDE_CODE_SESSION_ID:-unknown}" || echo "unknown"; git status --porcelain 2>/dev/null | head -20 || echo "clean"; git log --oneline -5 2>/dev/null || echo "no commits"

…this command is too complex to verify that it stays inside the worktree…

So the skill runner appears to concatenate all ! precompute commands into one shell
invocation. The guard's complexity heuristic rejects the compound form, even though every component
is individually safe and every one of them is read-only.

Blast radius

Counted across plugins/**/SKILL.md:

  • 172 ! precompute lines across 62 skills
  • 123 git-bearing precompute lines across 49 skills
  • 42 of those 49 declare two or more git precompute lines

Any skill with ≥2 precompute lines and ≥1 git command is affected, so the true count is at least 42.

This makes worktree-isolated agents unable to use the marketplace's own core workflow skills — which
is precisely the context those skills are most needed in, since CLAUDE.md and the worktree
convention push agents into worktrees for parallel work.

Impact

Fails closed and loudly, which is the good news — no silent degradation, no wrong output. But the
skill is simply unavailable, and the fallback is for the agent to hand-write whatever the skill would
have produced. In the session that found this, the handoff document had to be written by hand and
could not be placed in its canonical .work/handoffs/ location at all (see the second issue below).

Suggested directions (not verified end to end)

  1. Run precompute commands separately rather than joined. Each line already passes the guard on
    its own; if the runner issued them as N invocations, the problem disappears with no change to any
    skill. This looks like the correct fix and it is upstream of the marketplace.
  2. Failing that, simplify the precompute lines in the affected skills so the joined form stays
    within the guard's tolerance — e.g. drop the || echo "…" fallbacks, which mostly guard against a
    non-git directory that the guard itself already implies is not the case.
  3. Degrade rather than abort. A precompute that cannot run could yield an empty value and let the
    skill body proceed; every one of these lines is contextual, none is load-bearing for correctness.

Direction 1 is the only one that fixes all 42 skills at once, but it may live in the harness rather
than in this repository — worth confirming before any skill here is edited.

Related observation (separate, may warrant its own issue)

A cwd-pinned subagent also cannot call ExitWorktree:

ExitWorktree cannot be called from a subagent with a cwd override … This agent is already
isolated; use Bash with cd for directory changes within it.

Combined with the guard refusing writes to the shared checkout, an isolated agent has no path to
write a file that is meant to live outside its worktree — such as a handoff in .work/handoffs/,
which is deliberately shared so it survives worktree cleanup. In this session the worktree was
reclaimed by cleanup within minutes of the merge, so a handoff written inside it would have been
destroyed anyway.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    needs-humanHuman-in-the-loop required; autonomous sessions must not resolve items carrying this.priority: mediumReal value, no hard deadline; normal backlog flow.

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions