Skip to content

v1.33.2.0 fix: setup guards against Conductor worktree pollution of global install#1446

Merged
garrytan merged 2 commits into
mainfrom
garrytan/suva
May 12, 2026
Merged

v1.33.2.0 fix: setup guards against Conductor worktree pollution of global install#1446
garrytan merged 2 commits into
mainfrom
garrytan/suva

Conversation

@garrytan
Copy link
Copy Markdown
Owner

@garrytan garrytan commented May 12, 2026

Summary

./setup no longer pollutes the global install when run from a Conductor worktree of the gstack repo itself. Six-line bash guard at setup catches the BSD/macOS ln -snf footgun.

When the destination is an existing real directory (your global git clone), ln -snf SRC DST does NOT replace it — it creates DST/$(basename SRC) → SRC inside the dir. Each Conductor worktree's ./setup run was leaking a child symlink (dublin-v1, wellington, santiago-v1, ...) into ~/.claude/skills/gstack/, and Claude Code was picking each up as a separate top-level skill.

The guard resolves the existing real dir via pwd -P (mirroring how setup already resolves SOURCE_GSTACK_DIR). If it differs from the source, skip Claude registration and print a remediation hint. Binaries still build locally.

Test Coverage

8 tests in test/setup-conductor-worktree.test.ts (27 expect calls):

  • Static: guard exists in setup BEFORE ln -snf
  • Static: guard uses pwd -P to compare against $SOURCE_GSTACK_DIR
  • Static: skip branch contains remediation message
  • Behavioral: reproduces the BSD ln -snf child-symlink bug in a tmpdir
  • Behavioral: guard skips when dest is a real dir pointing elsewhere
  • Behavioral: guard allows ln when dest doesn't exist (fresh install)
  • Behavioral: guard allows ln when dest is an existing symlink (upgrade-in-place)
  • Behavioral: guard allows ln when dest is a real dir already pointing to source (self-rerun)

All 8 pass. Tests: 1100 → 1108 (+8 new).

Pre-Landing Review

No issues found. Diff is 30 lines net in setup plus the test file. All four pre-existing code paths through the affected branch are unchanged (fresh install, retarget existing symlink, self-rerun, --local).

Eval Results

No prompt-related files changed — evals skipped.

Plan Completion

No plan file — this was an ad-hoc fix triggered by manual cleanup during /gstack-upgrade.

TODOS

No TODO items completed in this PR.

Test plan

  • bun test test/setup-conductor-worktree.test.ts — 8/8 pass
  • bun test (full free suite) — exit 0
  • Manual verification: removed leaked symlinks from ~/.claude/skills/gstack/ in this session before fix
  • After merge: run ./setup from a sibling Conductor worktree and confirm it prints the skip message without polluting the global install

🤖 Generated with Claude Code


View in Codesmith
Need help on this PR? Tag @codesmith with what you need.

  • Let Codesmith autofix CI failures and bot reviews

garrytan and others added 2 commits May 11, 2026 20:21
…f the global install

Add a guard before `ln -snf "$SOURCE_GSTACK_DIR" "$HOME/.claude/skills/gstack"`
that detects whether the target already exists as a separate real directory.
On macOS/BSD, `ln -snf SRC DST` does not replace a real DST — it creates
DST/$(basename SRC) → SRC inside it. Running ./setup from each Conductor
worktree of the gstack repo was leaking per-worktree child symlinks into the
global install, which Claude Code then picked up as separate top-level skills.

The guard uses `cd ... && pwd -P` to resolve the existing real dir and compare
against the source (mirroring setup's own `SOURCE_GSTACK_DIR` resolution).
When they differ, prints a four-line remediation hint naming both paths and
exits the Claude registration branch cleanly. Binaries still build locally.

The four other code paths through this branch are unchanged: fresh install,
retarget an existing symlink, self-rerun where the existing dir resolves to
the same source, and --local installs.

Includes 8 tests covering static guard placement, `pwd -P` resolution, the
remediation message, a behavioral reproduction of the BSD `ln -snf` child-
symlink bug, and every branch of the guard (skip on real-dir-elsewhere, allow
on fresh, allow on existing symlink, allow on self-rerun).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown

E2E Evals: ✅ PASS

0/0 tests passed | $0 total cost | 12 parallel runners

Suite Result Status Cost

12x ubicloud-standard-2 (Docker: pre-baked toolchain + deps) | wall clock ≈ slowest suite

@garrytan garrytan merged commit dc6252d into main May 12, 2026
23 of 24 checks passed
Willardgmoore added a commit to Willardgmoore/gstack that referenced this pull request May 12, 2026
Brings in:
- v1.33.2.0: setup guard against Conductor worktree pollution (garrytan#1446)
- v1.33.1.0: learnings token-OR query + task-shaped retrieval (garrytan#1442)
- v1.33.0.0: /sync-gbrain memory stage batch-import refactor (garrytan#1432)

VERSION stays at 1.34.0.0 (no collision — queue-aware check confirms).
CHANGELOG: our [1.34.0.0] entry at top, three new main entries below.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant