Skip to content

Wave Execution Policy

Established 2026-05-10 from the Phase 233.2 retrospective. Governs how parallel gsd-executor agents are dispatched within a phase wave.

Phase 233.2 dispatched four parallel gsd-executor agents in Wave 2 (plans 02 / 03 / 04 / 05) with gsd-plan-checker having explicitly verified zero files_modified overlap. Despite that, Plan 04 self-aborted with all 10 of its files reverted to their pre-edit state. Plan 03 also got reverted once mid-execution but recovered.

The root cause is not file-level conflict — it is worktree-state-level conflict: when one executor’s commit-protocol cycle (git stash / git reset --hard HEAD) fires while another executor’s edits are still unstaged in the same worktree, the unstaged edits are wiped. The files_modified overlap check cannot detect this because the conflict is at the index/working-tree boundary, not at the file-content boundary.

Plan 02 happened to commit progressively (1–2 files per batch); it survived. Plan 04 batched its commits at the end of each task; it lost everything.

When dispatching multiple gsd-executor agents in the same wave on a shared worktree, exactly one of the following MUST apply:

Option 1 — Force commit-as-you-go (preferred for mechanical refactors)

Section titled “Option 1 — Force commit-as-you-go (preferred for mechanical refactors)”

Add an explicit clause to every parallel executor prompt:

COMMIT-AS-YOU-GO: to bound any potential clobber, commit after every 1–2 files (not after the whole task batch). Use the plan’s commit message convention. Run pnpm typecheck after each batch.

This is the lightest-weight option. It does not require infrastructure changes. The only downside is more commits in the squash-merge unit; this is harmless because squash-merge collapses them.

When to use: mechanical wrapper swaps, CSS/className migrations, codemod-style refactors. Anything where the executor naturally edits one file at a time.

Option 2 — Run sequentially in foreground

Section titled “Option 2 — Run sequentially in foreground”

Drop run_in_background=true from each Agent() call. Wait for plan N to complete before dispatching plan N+1.

When to use: the parallelism savings would be small (< 30% wall-clock), or the plans inherently produce overlapping edit ranges, or the wave has only 2 plans.

Option 3 — Isolated worktree per executor

Section titled “Option 3 — Isolated worktree per executor”

Pass isolation="worktree" when spawning each Agent() (the gsd-executor scaffold supports this). Each executor gets its own copy of the working tree; the orchestrator merges results sequentially as each agent completes.

When to use: the plans touch many files each, parallelism savings are large, and provisioning isolated worktrees is acceptable. This is the most robust option but has the highest setup cost.

  • DO NOT rely on files_modified non-overlap alone. That check is necessary but not sufficient. Two plans with disjoint file lists can still clobber each other through the worktree-state cycle described above.
  • DO NOT batch all commits at the end of the executor run when running in parallel on a shared worktree. That maximizes the clobber window.
  • DO NOT silently retry a self-aborted executor if the abort report mentions git reset --hard in the reflog. That signal means another agent’s commit-protocol fired during this agent’s edit window — re-running without changing the dispatch policy will recreate the same race.

If an executor reports any of these, the wave is suffering the clobber bug and the dispatch policy needs to change:

  • git reflog shows unexplained reset: moving to HEAD entries during the executor’s run.
  • Files the executor never touched appear in its git status output.
  • “Modified by user or linter” warnings on files the executor just edited, with the diff matching the file’s pre-edit state.

gsd-plan-checker should treat parallel-wave plans (wave: 2 with multiple wave: 2 siblings) as requiring an explicit dispatch-policy choice in the plan frontmatter or a parallelization-strategy block in the plan body. If neither is present, the checker should flag this as a HIGH advisory before execution.

  • Phase 233.2 retrospective: .planning/phases/233.2-card-system-migration-retire-shell-card-and-hero-shell-to-sh/233.2-RETROSPECTIVE.md
  • Phase 233.2 deferred items (logs the actual incident): .planning/phases/233.2-card-system-migration-retire-shell-card-and-hero-shell-to-sh/deferred-items.md
  • Memory entry for cross-session reuse: ~/.claude/projects/-Users-ordnas-Code-cinatra/memory/feedback_parallel_executor_clobber.md