fix(#683): auto-degrade phase execution to sequential on worktree base mismatch (#749)

* fix(#683): auto-degrade phase execution to sequential on worktree base mismatch

Claude Code forks worktree-isolated executors off the repository default
branch (origin/HEAD), not the orchestrator's HEAD. Running /gsd-execute-phase
on a branch diverged from the default (unmerged milestone/feature branch) left
every executor without the phase's plan files and tripped the
worktree-branch-check guard with `exit 42` — 100% reproducible, all OSes.

- New module src/worktree-base-ref.cts: HEAD-vs-fork-base drift detection
  (origin/HEAD with symbolic-ref fallback) and no-clobber worktree.baseRef
  management, exposed as `worktree base-check` / `worktree set-baseref`.
- execute-phase.md: pre-dispatch, for Claude Code with worktrees enabled,
  auto-degrades the run to sequential on the main tree when a base mismatch
  is detected, recommending worktree.baseRef:"head". The exit-42 guard stays
  as a backstop.
- Installer: fresh local Claude installs set worktree.baseRef:"head" in
  .claude/settings.local.json (no-clobber, respecting an explicit shared
  settings.json value); upgrades print an opt-in notice pointing at
  `gsd-tools worktree set-baseref`.
- Docs: how-to guide, CLI/config reference, planning-config cross-ref.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix(#683): auto-apply worktree.baseRef on upgrade; gate fresh+upgrade on use_worktrees

Per maintainer direction: on a local Claude Code UPGRADE, set
worktree.baseRef:"head" automatically (no opt-in notice) when the project's
workflow.use_worktrees is enabled, instead of merely printing a remediation
notice. For consistency the FRESH path is now gated the same way: both paths
compute worktrees-enabled once (bounded walk-up read of .planning/config.json,
default enabled unless workflow.use_worktrees === false) and apply the
no-clobber baseRef only when enabled — never overwriting an explicit value in
settings.local.json or a shared settings.json. gsd-tools worktree set-baseref
remains for manual use. Docs + changeset updated; tests hardened (file-exists
assertions, fresh+disabled case, upgrade idempotency).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix(#683): measure workflow byte-budget on LF, fixing Windows-only CI failure

The workflow-size-budget test failed only on Windows: git checks out the .md
files as CRLF (no eol=lf in .gitattributes) and byteCount used
fs.statSync().size (raw on-disk bytes), counting an extra \r per line. That
inflated execute-phase.md — the XL high-water-mark file pinned near its ceiling
by the tighten-only ratchet — from 88492 LF bytes to ~90245 on Windows, over
the 90000 XL ceiling, while passing on the LF-checkout Mac/Linux runners.

The ceilings are explicitly "calibrated against raw `wc -c`" on an LF checkout,
so the measurement should be LF-based on every platform. byteCount now reads the
file and counts Buffer.byteLength after stripping CR, making the budget
platform-independent (a no-op on LF checkouts; verified statSync === normalized
for all 88 workflow files). No ceilings changed. Added a regression test
asserting CRLF and LF content of the same file count identically.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix(#683): make worktree-base-ref test path mocks Windows-safe (path.join)

tests/worktree-base-ref.test.cjs keyed its injected readFile/writeFile mocks
(and a few expected `file` values) with forward-slash template literals like
`${claudeDir}/settings.local.json`. The module composes those paths with
path.join(), which emits backslashes on Windows, so the mock keys never matched
the module's lookup → readFile returned null → resolveEffectiveBaseRef /
cmdWorktreeBaseCheck / cmdWorktreeSetBaseRef (and the JSONC variants) failed on
the Windows full-test runner only (they passed on Mac/Linux, and the install
tests passed because they use the real filesystem). The module is correct;
only the test fixtures hardcoded '/'.

All mock keys and path assertions now use path.join(base, ...) mirroring the
module, so they match on every platform (no-op on POSIX). 19 path references
across 16 lines.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Tom Boucher
2026-06-06 23:40:24 -04:00
committed by GitHub
parent edd3c6c332
commit cf8bd3cd5e
18 changed files with 2077 additions and 9 deletions

View File

@@ -421,6 +421,34 @@ node gsd-tools.cjs websearch <query> [--limit N] [--freshness day|week|month]
---
## Worktree Commands
Diagnose and configure the worktree fork base used by Claude Code's `isolation="worktree"` executor dispatch. These commands address the branch-divergence condition described in [Fix the worktree base-mismatch (exit 42) error](how-to/fix-worktree-base-mismatch.md).
```bash
# Check whether the current HEAD has diverged from the worktree fork base.
# Returns JSON: { shouldDegrade, reason, message, headSha, forkRef, forkSha }
node gsd-tools.cjs worktree base-check
# Write worktree.baseRef:"head" into .claude/settings.local.json (no-clobber).
# Returns JSON: { changed, skipped, previous, baseRef, file }
node gsd-tools.cjs worktree set-baseref
```
**`worktree base-check`** reads `worktree.baseRef` from `.claude/settings.local.json` (then `.claude/settings.json`) and compares the current `HEAD` SHA against `origin/HEAD`. The `shouldDegrade` field is `true` when the execute-phase orchestrator will fall back to sequential execution. Possible `reason` values:
| `reason` | `shouldDegrade` | Meaning |
|---|---|---|
| `baseref-head` | `false` | `worktree.baseRef:"head"` is set; no mismatch possible |
| `head-matches-fork` | `false` | HEAD and `origin/HEAD` are the same commit |
| `head-diverged-from-fork` | `true` | Branch is ahead of or diverged from `origin/HEAD` |
| `fork-ref-unknown` | `true` | `origin/HEAD` could not be resolved |
| `no-head` | `false` | Not in a git repo (no `HEAD`) |
**`worktree set-baseref`** applies a no-clobber write of `worktree.baseRef:"head"` to `.claude/settings.local.json`. If the file already contains an explicit `baseRef` value other than `"head"`, the existing value is preserved and `skipped:"explicit-other"` is returned. Malformed JSON causes an error rather than a silent overwrite. Both fresh installs and upgrades of GSD Core run this automatically when `workflow.use_worktrees` is enabled (the default); the command is also available for manual use — for example, to apply the setting when worktrees were toggled on after installation, or to re-apply it after a settings change.
---
## Graphify
Build, query, and inspect the project knowledge graph in `.planning/graphs/`. Requires `graphify.enabled: true` in `config.json` (see [Configuration Reference](CONFIGURATION.md#graphify-settings)).
@@ -471,6 +499,7 @@ User-facing entry point: `/gsd-graphify` (see [Command Reference](COMMANDS.md#gs
| Audit | `lib/audit.cjs` | Phase/milestone audit queue handlers; `audit-open` helper |
| GSD2 Import | `lib/gsd2-import.cjs` | Reverse-migration importer from GSD-2 projects (backs `/gsd-import --from-gsd2`) |
| Intel | `lib/intel.cjs` | Queryable codebase intelligence index (backs `/gsd-map-codebase --query`) |
| Worktree Base Ref | `lib/worktree-base-ref.cjs` | Worktree fork-base detection and `worktree base-check` / `set-baseref` commands (#683) |
---
@@ -498,4 +527,5 @@ API keys configured via `/gsd-settings` (`brave_search`, `firecrawl`, `exa_searc
- [Commands](COMMANDS.md)
- [Configuration](CONFIGURATION.md)
- [Architecture](ARCHITECTURE.md)
- [Fix the worktree base-mismatch (exit 42) error](how-to/fix-worktree-base-mismatch.md)
- [docs index](README.md)

View File

@@ -245,7 +245,7 @@ All workflow toggles follow the **absent = enabled** pattern. If a key is missin
| `workflow.max_discuss_passes` | number | `3` | Maximum number of question rounds in discuss-phase before the workflow stops asking. Useful in headless/auto mode to prevent infinite discussion loops. |
| `workflow.skip_discuss` | boolean | `false` | When `true`, `/gsd-autonomous` bypasses the discuss-phase entirely, writing minimal CONTEXT.md from the ROADMAP phase goal. Useful for projects where developer preferences are fully captured in PROJECT.md/REQUIREMENTS.md. Added in v1.28 |
| `workflow.text_mode` | boolean | `false` | Replaces AskUserQuestion TUI menus with plain-text numbered lists. Required for Claude Code remote sessions (`/rc` mode) where TUI menus don't render. Can also be set per-session with `--text` flag on discuss-phase. Added in v1.28 |
| `workflow.use_worktrees` | boolean | `true` | When `false`, disables git worktree isolation for parallel execution. Users who prefer sequential execution or whose environment does not support worktrees can disable this. Added in v1.31 |
| `workflow.use_worktrees` | boolean | `true` | When `false`, disables git worktree isolation for parallel execution. Users who prefer sequential execution or whose environment does not support worktrees can disable this. Added in v1.31. **Branch-divergence note:** when your branch is ahead of `origin/HEAD`, GSD auto-degrades to sequential and prints a warning. Set `worktree.baseRef:"head"` in `.claude/settings.local.json` (run `node gsd-tools.cjs worktree set-baseref`) to restore parallel execution. See [Fix the worktree base-mismatch (exit 42) error](how-to/fix-worktree-base-mismatch.md). |
| `workflow.worktree_skip_hooks` | boolean | `false` | When `true`, executor agents in worktree mode pass `--no-verify` (skipping pre-commit hooks) and post-wave hook validation runs against the merged result instead. Opt-in escape hatch for projects whose hooks cannot run in agent worktrees. Default `false` runs hooks on every commit (#2924). |
| `workflow.code_review` | boolean | `true` | Enable `/gsd-code-review` and `/gsd-code-review --fix` commands. When `false`, the commands exit with a configuration gate message. Added in v1.34 |
| `workflow.code_review_depth` | string | `standard` | Default review depth for `/gsd-code-review`: `quick` (pattern-matching only), `standard` (per-file analysis), or `deep` (cross-file with import graphs). Can be overridden per-run with `--depth=`. Added in v1.34 |

View File

@@ -1,5 +1,5 @@
{
"generated": "2026-06-06",
"generated": "2026-06-07",
"families": {
"agents": [
"gsd-advisor-researcher",
@@ -350,6 +350,7 @@
"workstream-inventory.cjs",
"workstream-name-policy.cjs",
"workstream.cjs",
"worktree-base-ref.cjs",
"worktree-safety.cjs"
],
"hooks": [

View File

@@ -370,7 +370,7 @@ The `gsd-planner` agent is decomposed into a core agent plus reference modules t
---
## CLI Modules (86 shipped)
## CLI Modules (87 shipped)
Full listing: `gsd-core/bin/lib/*.cjs`.
@@ -461,6 +461,7 @@ Full listing: `gsd-core/bin/lib/*.cjs`.
| `workstream-inventory.cjs` | Shared workstream inventory projection: state fields, phase/plan/summary counts, roadmap phase count, and active marker — thin orchestrator that delegates pure projection to `workstream-inventory-builder.cjs` |
| `workstream-name-policy.cjs` | Canonical workstream name validation (`isValidActiveWorkstreamName`, `hasInvalidPathSegment`, `validateWorkstreamName`) and slug normalization (`toWorkstreamSlug`) |
| `workstream.cjs` | Workstream CRUD, migration, session-scoped active pointer |
| `worktree-base-ref.cjs` | Worktree base-ref drift detection and degrade decision (`evaluateWorktreeBaseDegrade`) plus no-clobber `worktree.baseRef` settings management for the `base-check`/`set-baseref` subcommands (#683) |
| `worktree-safety.cjs` | Worktree-root resolution and non-destructive prune policy decisions; owns W017 health-check logic |
[`docs/CLI-TOOLS.md`](CLI-TOOLS.md) may describe a subset of these modules; when it disagrees with the filesystem, this table and the directory listing are authoritative.

View File

@@ -33,6 +33,7 @@ Language versions: [English](README.md) · [Português (pt-BR)](pt-BR/README.md)
- [Migrate from GSD 2](how-to/migrate-from-gsd-2.md) — upgrade an existing GSD 2 project to GSD Core
- [Update GSD](how-to/update-gsd.md) — re-run the installer to pick up the latest release
- [Clean up get-shit-done-cc](cleanup-get-shit-done-cc.md) — remove leftover old-package artifacts that cause a spurious `⬆ /gsd:update` indicator after migrating to `@opengsd/gsd-core`
- [Fix the worktree base-mismatch (exit 42) error](how-to/fix-worktree-base-mismatch.md) — resolve the branch-divergence condition that halts parallel phase execution
- [Recover and troubleshoot](how-to/recover-and-troubleshoot.md) — fix common problems, rebuild context, and uninstall
---

View File

@@ -0,0 +1,143 @@
# How to fix the worktree base-mismatch (exit 42) error
**Goal:** Understand why `/gsd-execute-phase` halts with `FATAL: worktree base mismatch` / exit 42 when your branch is ahead of the default branch, and choose the right fix to restore normal — or parallel — execution.
**Prerequisites:** GSD Core is installed and you have an active project. You have run `/gsd-execute-phase` and either seen the exit-42 error or the one-line `⚠ Worktree base mismatch` warning.
---
## What you will see
When you run `/gsd-execute-phase` on a branch that is ahead of the repository's default branch (for example, an unmerged milestone branch, a long-lived feature branch, or a branch with commits not yet in `origin/HEAD`), you may see one of two messages:
**Automatic-degrade warning (phase still completes):**
```
⚠ Worktree base mismatch: HEAD (abc12345) differs from origin/HEAD (def67890).
Running this phase sequentially on the main working tree.
To keep parallel worktrees, set worktree.baseRef:"head" in
.claude/settings.local.json (or run: gsd-tools worktree set-baseref). See #683.
```
The phase runs to completion sequentially; nothing is blocked. This is the runtime mitigation.
**Exit-42 halt (older installs or misconfigured environments):**
```
FATAL: worktree base mismatch
```
All worktree-isolated executors halt immediately. Zero progress is made.
---
## Why this happens
Claude Code's `isolation="worktree"` forks executor worktrees from the repository's default branch (`origin/HEAD`), not from your current `HEAD`. When your branch contains commits that `origin/HEAD` does not have — plan files, new source files, anything added since the branch diverged — those files are absent inside each worktree. GSD's `worktree-branch-check` safety guard correctly refuses to act on a worktree that does not match the orchestrator's state, and exits with code 42.
This is the guard working as designed: it prevents silent data loss or phantom edits in the wrong tree. The error is a branch-state condition, not an OS-specific or hardware issue.
---
## Option 1 — Do nothing (you are already unblocked)
If you saw the `⚠ Worktree base mismatch` warning rather than an exit-42 halt, GSD has already automatically degraded to sequential execution on the main working tree for this run. The phase will complete. No action is required.
Use this option when:
- You are on a diverged branch temporarily
- You do not care about parallel execution for this phase
- You want to merge back to the default branch soon
---
## Option 2 — Permanent fix: set `worktree.baseRef: "head"` (recommended)
This option restores parallel worktree execution on diverged branches. It tells Claude Code to fork executor worktrees from your current `HEAD` instead of `origin/HEAD`, so the plan files and branch-only commits are present in every worktree.
Run the convenience command from your project root:
```bash
node "$HOME/.claude/gsd-core/bin/gsd-tools.cjs" worktree set-baseref
```
This writes `worktree.baseRef: "head"` into `.claude/settings.local.json` in your project root. It is no-clobber: if you already have an explicit `baseRef` set to something else, it leaves your value in place and tells you.
To verify the result:
```bash
node "$HOME/.claude/gsd-core/bin/gsd-tools.cjs" worktree base-check
```
The output is JSON. When `shouldDegrade` is `false` and `reason` is `"baseref-head"`, parallel worktrees will work on any branch.
Alternatively, set the value by hand in `.claude/settings.local.json`:
```json
{
"worktree": {
"baseRef": "head"
}
}
```
**Note:** Fresh installs and upgrades of GSD Core both set `worktree.baseRef:"head"` automatically in `.claude/settings.local.json` (no-clobber) when `workflow.use_worktrees` is enabled (the default). You can also apply or re-apply it manually at any time with `gsd-tools worktree set-baseref` — for example, if you toggled worktrees on after the initial install.
Use this option when:
- You regularly work on long-lived or milestone branches
- You want parallel phase execution (faster, lower context-window pressure)
- You are a solo developer or team working on a feature branch for an extended period
---
## Option 3 — Fallback: disable worktrees entirely
If worktrees are causing persistent problems beyond the base-mismatch (for example, your environment does not support them), disable them permanently for this project:
Add or edit `.planning/config.json`:
```json
{
"workflow": {
"use_worktrees": false
}
}
```
All executor agents will then run sequentially on the main working tree for every phase. This is equivalent to what the automatic degrade does, but permanent.
Use this option when:
- Worktrees are consistently problematic in your environment
- You prefer sequential execution for auditability or tooling reasons
- You are on a platform or CI setup that does not support git worktrees
See also: [`workflow.use_worktrees`](../CONFIGURATION.md#workflow-toggles) in the configuration reference.
---
## The exit-42 backstop
The `worktree-branch-check` guard (exit 42) remains active in all execution modes as a safety backstop. It fires only when an executor worktree's branch does not match the expected orchestrator state — a condition that should not arise once you have applied one of the options above. If you continue to see exit 42 after setting `worktree.baseRef: "head"`, run `/gsd-forensics` to investigate.
---
## Summary
| Situation | Recommended action |
|-----------|-------------------|
| Saw the warning, phase completed | Nothing — degrade handled it automatically |
| Regularly on diverged branches, want parallel execution | `worktree set-baseref` (Option 2) |
| Worktrees consistently problematic | Set `workflow.use_worktrees: false` (Option 3) |
| Still seeing exit 42 after fixes | Run `/gsd-forensics "exit 42 after fix"` |
---
## Related
- [Recover and troubleshoot](recover-and-troubleshoot.md)
- [Debug a failed execution](debug-a-failed-execution.md)
- [Configuration reference — workflow toggles](../CONFIGURATION.md#workflow-toggles)
- [CLI Tools reference — worktree commands](../CLI-TOOLS.md#worktree-commands)
- [docs index](../README.md)

View File

@@ -153,6 +153,18 @@ Check whether the plan is too ambitious. Plans should have two or three tasks at
For systematic diagnosis of what went wrong, see [Debug a failed execution](debug-a-failed-execution.md).
### If you see "FATAL: worktree base mismatch" or the exit-42 warning
This happens when your current branch is ahead of the repository's default branch (for example, an unmerged milestone or feature branch). Claude Code forks executor worktrees from `origin/HEAD`, not your `HEAD`, so plan files that exist only on your branch are absent inside the worktree.
Since the fix landed, GSD automatically degrades to sequential execution on the main working tree and prints a one-line warning — the phase will complete without any action from you. To restore parallel execution permanently, run:
```bash
node "$HOME/.claude/gsd-core/bin/gsd-tools.cjs" worktree set-baseref
```
For a full explanation and all available options, see [Fix the worktree base-mismatch (exit 42) error](fix-worktree-base-mismatch.md).
### If parallel execution causes build lock errors or pre-commit hook failures
This is caused by multiple agents triggering build tools simultaneously. GSD handles this automatically since v1.26. If you are on an older version, or still seeing contention, disable parallel execution:
@@ -312,12 +324,14 @@ Also audit which MCP servers are enabled. Every enabled MCP server injects its t
| Update broke local changes | `/gsd-update --reapply` |
| Want session summary | `/gsd-pause-work --report` |
| Parallel execution build errors | Update GSD or set `parallelization.enabled: false` |
| Worktree base mismatch / exit 42 | Auto-degraded to sequential (no action needed); run `worktree set-baseref` to restore parallelism |
---
## Related
- [Debug a failed execution](debug-a-failed-execution.md)
- [Fix the worktree base-mismatch (exit 42) error](fix-worktree-base-mismatch.md)
- [Install on your runtime](install-on-your-runtime.md)
- [Commands](../COMMANDS.md)
- [docs index](../README.md)