Skip to content

swarm-implement and codebase-review-swarm tell subagents to git stash, which destroys sibling agents' work in a shared worktree #1970

Description

@zaxbysauce

Summary

.opencode/skills/swarm-implement/SKILL.md:78 (Phase 0b) and the byte-identical instruction at .opencode/skills/codebase-review-swarm/SKILL.md:74 say:

Verify the working tree is clean with git status --porcelain. If uncommitted changes exist, stash them or abort the checkout to prevent data loss.

That is safe for a single agent and destructive under the execution model these same skills mandate. swarm-implement Phase 1 instructs "Launch parallel subagents for disjoint investigation tasks"; those agents share one worktree. git stash is worktree-global, not scoped to the caller's files — so an agent following Phase 0b stashes every sibling agent's in-flight work.

Note the instruction's own framing: it exists "to prevent data loss." Under parallel execution it causes the data loss it was written to prevent.

This is not hypothetical — it fired

During implementation of the PR-workflow lane-output fix (branch claude/swarm-pr-gates-restrictive-vnu6fc), five agents were editing disjoint file scopes concurrently. One ran git stash to take its own clean-tree baseline measurement, followed by a git reset. That stashed all twelve modified files across all five agents — ~451 insertions of completed, unrelated work.

It was caught only because a source grep for a symbol one agent had reported adding came back empty. The agent's completion report was truthful; its work had been silently removed underneath it by a sibling. Recovered in full via git stash pop, but only because it was noticed within the same session — nothing in the protocol would have surfaced it, and a later git stash would have buried the first stash entry deeper.

Failure mode worth stressing: every agent reports success, and the work is gone. There is no error, no failed assertion, and no gate that detects it. The only signal is an independent re-read of the file.

Why the fix is well-defined

The repository already contains the correct pattern in three places:

  • .opencode/skills/running-tests/SKILL.md:202"Use a Git worktree — safer than git stash (stash can drop untracked…)"
  • .opencode/skills/swarm-pr-review/SKILL.md:211 and .opencode/skills/swarm-pr-feedback/SKILL.md:174"Do not issue git stash through shell", with checkout preparation owned by the prepare_pr_workflow_checkout controller tool instead of delegated agents

So the PR workflow already solved exactly this problem: the orchestrator owns worktree state transitions, delegated agents are forbidden from performing them, and there is a dedicated tool for the legitimate case. The generic implementation skills never inherited that lesson.

Proposed change

  1. swarm-implement/SKILL.md Phase 0b and codebase-review-swarm/SKILL.md — restrict worktree state transitions (stash, reset, checkout ., restore) to the orchestrator, before any subagent is dispatched. Delegated agents must never run them. Point at a git worktree (running-tests precedent) or a temporary save branch (commit-pr/SKILL.md:80 already prefers this on Windows) rather than stash.
  2. Add an explicit prohibition to the delegation contract so it reaches agents that never read Phase 0b. In this session the workaround was putting a hard "NEVER run git stash/reset/checkout ./restore" rule in every subagent prompt; that worked, and belongs in the skill rather than in ad-hoc prompts.
  3. Consider a mechanical guard. A tool.execute.before hook rejecting destructive git verbs from non-orchestrator sessions would make this structurally impossible instead of merely documented — mirroring how the PR-workflow gate already blocks these. Optional, but this is a silent-data-loss class, which the guardrail ladder (docs/engineering-invariants.md) treats as warranting machinery over vigilance.

Acceptance criteria

  • Neither swarm-implement nor codebase-review-swarm instructs a delegated agent to stash; both name the orchestrator-owned alternative.
  • The delegation contract carries an explicit destructive-git prohibition reaching agents that never read Phase 0b.
  • Guidance is consistent with running-tests:202, swarm-pr-review:211, swarm-pr-feedback:174, and commit-pr:80.
  • bun run drift:check clean (both files are canonical .opencode sources with .claude/.agents adapter shims).
  • Release fragment under docs/releases/pending/ (AGENTS.md invariant 12).
  • If the hook guard in (3) is implemented: wired per invariant 11 and covered by a test proving a delegated session cannot run git stash.

Notes

Both files are canonical .opencode sources classified in src/config/skill-mirrors.ts; the .claude/.agents copies are thin adapter shims and should not need content edits.

Surfaced while implementing the fix on claude/swarm-pr-gates-restrictive-vnu6fc. Unrelated to that change's subject matter — filed separately rather than folded in.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions